introduced uiText.ts
This commit is contained in:
@@ -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 && (
|
||||
|
||||
@@ -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;
|
||||
Reference in New Issue
Block a user