introduced uiText.ts

This commit is contained in:
2026-07-20 16:44:21 +02:00
parent f2ab2b31ed
commit 444ce804ed
5 changed files with 63 additions and 20 deletions
@@ -1,6 +1,7 @@
import { useEffect, useRef, useState, type SyntheticEvent } from 'react'
import { login } from '../../api/connections'
import type { LoginRequest, ServiceType } from '../../types/connection'
import { uiText } from '../../text/uiText'
import '../ui/Modal.scss'
interface Props {
@@ -44,7 +45,7 @@ export function ConnectModal({ serviceType, label, onClose, onSuccess }: Readonl
await login(req)
onSuccess()
} catch (err) {
setError(err instanceof Error ? err.message : 'Login failed')
setError(err instanceof Error ? err.message : uiText.connections.connectModal.loginFailed)
} finally {
setLoading(false)
}
@@ -53,31 +54,31 @@ export function ConnectModal({ serviceType, label, onClose, onSuccess }: Readonl
return (
<dialog className="modal" ref={dialogRef}>
<div className="modal__header">
<h2 className="modal__title" id="modal-title">Connect to {label}</h2>
<button className="modal__close" onClick={onClose} aria-label="Close">×</button>
<h2 className="modal__title" id="modal-title">{uiText.connections.connectModal.title(label)}</h2>
<button className="modal__close" onClick={onClose} aria-label={uiText.modal.closeAriaLabel}>{uiText.modal.closeSymbol}</button>
</div>
<form className="modal__form" onSubmit={handleSubmit}>
<div className="modal__field">
<label className="modal__label" htmlFor="appUrl">App URL</label>
<label className="modal__label" htmlFor="appUrl">{uiText.connections.connectModal.appUrlLabel}</label>
<input id="appUrl" ref={firstInputRef} className="modal__input" type="url"
placeholder="https://homebox.example.com"
placeholder={uiText.connections.connectModal.appUrlPlaceholder}
value={appUrl} onChange={e => setAppUrl(e.target.value)} required />
</div>
<div className="modal__field">
<label className="modal__label" htmlFor="username">Username</label>
<label className="modal__label" htmlFor="username">{uiText.connections.connectModal.usernameLabel}</label>
<input id="username" className="modal__input" type="text"
autoComplete="username"
value={username} onChange={e => setUsername(e.target.value)} required />
</div>
<div className="modal__field">
<label className="modal__label" htmlFor="password">Password</label>
<label className="modal__label" htmlFor="password">{uiText.connections.connectModal.passwordLabel}</label>
<input id="password" className="modal__input" type="password"
autoComplete="current-password"
value={password} onChange={e => setPassword(e.target.value)} required />
</div>
{error && <p className="modal__error">{error}</p>}
<button className="modal__submit" type="submit" disabled={loading}>
{loading ? 'Connecting…' : 'Connect'}
{loading ? uiText.connections.connectModal.connectingButton : uiText.connections.connectModal.connectButton}
</button>
</form>
</dialog>
@@ -5,6 +5,7 @@ import { ServiceCard } from "./ServiceCard"
import { ConnectModal } from "./ConnectModal"
import "./Dashboard.scss"
import { SearchModal } from "../search/SearchModal"
import { uiText } from "../../text/uiText"
const SERVICES = [
{ serviceType: ServiceType.HOMEBOX, label: 'Homebox', icon: '📦' },
@@ -32,10 +33,10 @@ export function Dashboard() {
return (
<div className="dashboard">
<div className="dashboard__header">
<h1 className="dashboard__title">Vaessl Dashboard</h1>
<h1 className="dashboard__title">{uiText.dashboard.title}</h1>
</div>
<p className="dashboard__section-label">Services</p>
<p className="dashboard__section-label">{uiText.dashboard.servicesLabel}</p>
<div className="dashboard__cards">
{SERVICES.map(({ serviceType, label, icon }) => (
<ServiceCard
@@ -1,5 +1,6 @@
import type { ConnectionStatus, ServiceType } from '../../types/connection'
import { ActionButton } from '../ui/ActionButton'
import { uiText } from '../../text/uiText'
import './ServiceCard.scss'
interface Props {
@@ -29,20 +30,20 @@ export function ServiceCard({ serviceType: _serviceType, label, icon, status, on
<p className="service-card__name">{label}</p>
<p className="service-card__meta">
<span className={`service-card__badge service-card__badge--${connected ? 'connected' : 'disconnected'}`}>
{connected ? 'Connected' : 'Not connected'}
{connected ? uiText.connections.serviceCard.connected : uiText.connections.serviceCard.notConnected}
</span>
{connected && status?.username && <span>{status.username}</span>}
{connected && status?.expiresAt && (
<span>· expires {formatExpiry(status.expiresAt)}</span>
<span>{uiText.connections.serviceCard.expiresPrefix(formatExpiry(status.expiresAt) ?? '')}</span>
)}
</p>
</div>
</div>
<div className="service-card__actions">
<ActionButton variant={connected ? 'disconnect' : 'connect'} onClick={connected ? onDisconnect : onConnect}>
{connected ? 'Disconnect' : 'Connect'}
{connected ? uiText.connections.serviceCard.disconnectButton : uiText.connections.serviceCard.connectButton}
</ActionButton>
{connected && (<ActionButton variant='search' onClick={onSearch}>Search</ActionButton>)}
{connected && (<ActionButton variant='search' onClick={onSearch}>{uiText.connections.serviceCard.searchButton}</ActionButton>)}
</div>
</div>
)
@@ -3,6 +3,7 @@ import '../ui/Modal.scss'
import { type PagedSearchResponse, type ServiceItem, type SearchRequest, type SyncRequest } from '../../types/search'
import { syncVectorData, search } from '../../api/searches'
import type { ServiceType } from '../../types/connection'
import { uiText } from '../../text/uiText'
interface Props {
serviceType: ServiceType
@@ -48,7 +49,7 @@ export function SearchModal({ serviceType, label, appUrl, username, onClose }: R
const res = await search(req)
setResults(res)
} catch (err) {
setSearchError(err instanceof Error ? err.message : 'Search failed')
setSearchError(err instanceof Error ? err.message : uiText.search.searchFailed)
} finally {
setLoading(false)
}
@@ -61,7 +62,7 @@ export function SearchModal({ serviceType, label, appUrl, username, onClose }: R
const req: SyncRequest = { appUrl, serviceType, username}
await syncVectorData(req)
} catch (err) {
setSyncError(err instanceof Error ? err.message: 'Refreshing data failed')
setSyncError(err instanceof Error ? err.message: uiText.search.syncFailed)
} finally {
setLoading(false)
}
@@ -70,8 +71,8 @@ export function SearchModal({ serviceType, label, appUrl, username, onClose }: R
return (
<dialog className='modal' ref={dialogRef}>
<div className='modal__header'>
<h2 className='modal__title' id='modal-title'>Search in {label}</h2>
<button className='modal__close' onClick={onClose} aria-label='Close'>×</button>
<h2 className='modal__title' id='modal-title'>{uiText.search.title(label)}</h2>
<button className='modal__close' onClick={onClose} aria-label={uiText.modal.closeAriaLabel}>{uiText.modal.closeSymbol}</button>
</div>
<form className='modal__form' onSubmit={handleSubmit}>
<div className='modal__field'>
@@ -81,8 +82,8 @@ export function SearchModal({ serviceType, label, appUrl, username, onClose }: R
{searchError && <p className='modal__error'>{searchError}</p>}
{syncError && <p className='modal__error'>{syncError}</p>}
<div className='modal__actions'>
<button className='modal__other' title={`Syncs your ${serviceType} database for vectorization`} type='button' disabled={loading} onClick={handleSync}>Refresh Data</button>
<button className='modal__submit' type='submit' disabled={loading}>Search</button>
<button className='modal__other' title={uiText.search.syncTooltip(label)} type='button' disabled={loading} onClick={handleSync}>{uiText.search.refreshDataButton}</button>
<button className='modal__submit' type='submit' disabled={loading}>{uiText.search.searchButton}</button>
</div>
</form>
{results && (
+39
View File
@@ -0,0 +1,39 @@
export const uiText = {
modal: {
closeAriaLabel: "Close",
closeSymbol: "×",
},
dashboard: {
title: "Vaessl Dashboard",
servicesLabel: "Services",
},
connections: {
connectModal: {
title: (label: string) => `Connect to ${label}`,
appUrlLabel: "App URL",
appUrlPlaceholder: "https://homebox.example.com",
usernameLabel: "Username",
passwordLabel: "Password",
loginFailed: "Login failed",
connectButton: "Connect",
connectingButton: "Connecting…",
},
serviceCard: {
connected: "Connected",
notConnected: "Not connected",
expiresPrefix: (date: string) => `· expires ${date}`,
disconnectButton: "Disconnect",
connectButton: "Connect",
searchButton: "Search",
},
},
search: {
title: (label: string) => `Search in ${label}`,
searchFailed: "Search failed",
syncFailed: "Refreshing data failed",
syncTooltip: (label: string) =>
`Syncs your ${label} database for vectorization`,
refreshDataButton: "Refresh Data",
searchButton: "Search",
},
} as const;