style and translate dashboard

This commit is contained in:
Roland Osborne 2024-03-01 13:33:19 -08:00
parent 2541417662
commit 71d6598556
8 changed files with 371 additions and 234 deletions

View File

@ -107,6 +107,48 @@ export const en = {
createMessage: 'Please check with your administrator.',
adminError: 'Admin Access Error',
adminMessage: 'Please confirm your password.',
confirmDelete: 'Deleting Account',
areSure: 'Are you sure you want to delete the account?',
mb: 'MB',
gb: 'GB',
copied: 'Copied',
accounts: 'Accounts',
accessAccount: 'Access Account',
createAccount: 'Create Account',
browserLink: 'Browser Link',
mobileToken: 'Mobile Token',
createLink: 'Create Account Link',
configureServer: 'Configure Server',
reloadAccounts: 'Reload Accounts',
disableAccount: 'Disable Account',
enableAccount: 'Enable Account',
deleteAccount: 'Delete Account',
settings: 'Settings',
hostHint: 'domain:port/app',
federatedHost: 'Federated Host',
storageLimit: 'Storage Limit (GB) / Account',
storageHint: '0 for Unlimited',
keyType: 'Account Key Type',
accountCreation: 'Public Account Creation',
enablePush: 'Enable Push Notifications',
allowUnsealed: 'Allow Unsealed Topics',
topicContent: 'Topic Content:',
enableImage: 'Enable Image Queue',
imageHint: 'Allow images to be posted in topics',
enableAudio: 'Enable Audio Queue',
audioHint: 'Allow audio to be posted in topics',
enableVideo: 'Enable Video Queue',
videoHint: 'Allow video to be posted in topics',
enableWeb: 'Enable WebRTC Calls',
webHint: 'Enable audio and video calls to contacts',
serverUrl: 'WebRTC Server URL',
urlHint: 'turn:ip:port?transport=udp',
webUsername: 'WebRTC Username',
webPassword: 'WebRTC Password',
failedLoad: 'Failed to Load',
limit: 'Limit',
};
export const fr = {
@ -218,5 +260,47 @@ export const fr = {
createMessage: 'Veuillez vérifier auprès de votre administrateur.',
adminError: 'Erreur d\'Accès',
adminMessage: 'Veuillez confirmer votre mot de passe',
confirmDelete: 'Suppression de Compte',
areSure: 'Êtes-vous sûr de vouloir supprimer le compte?',
mb: 'Mo',
gb: 'Go',
copied: 'Copié',
accounts: 'Comptes',
accessAccount: 'Accéder au Compte',
createAccount: 'Créer un Compte',
browserLink: 'Lien du Navigateur',
mobileToken: 'Code Mobile',
createLink: 'Lien pour Créer un Compte',
configureServer: 'Configurer le Serveur',
reloadAccounts: 'Recharger les Comptes',
disableAccount: 'Désactiver le Compte',
enableAccount: 'Activer le Compte',
deleteAccount: 'Supprimer le Compte',
settings: 'Paramètres',
hostHint: 'domaine:port/app',
federatedHost: 'Hôte Fédéré',
storageLimit: 'Limite de Espace (Go) / Compte',
storageHint: '0 pour Illimité',
keyType: 'Type de Clé',
accountCreation: 'Création de Compte Public',
enablePush: 'Activer les Notifications Push',
allowUnsealed: 'Autoriser les Sujets non Sécurisés',
topicContent: 'Contenu du Sujet:',
enableImage: 'Activer les Images du Sujet',
imageHint: 'Autoriser la publication d\'images dans des sujets',
enableAudio: 'Activer l\'Audio du Suject',
audioHint: 'Autoriser la publication d\'audio dans des sujets',
enableVideo: 'Activer les Videos du Sujet',
videoHint: 'Autoriser la publication de video dans des sujets',
enableWeb: 'Activer les Appels WebRTC',
webHint: 'Autoriser les appels audio et vidéo aux contacts',
serverUrl: 'URL du Serveur WebRTC',
urlHint: 'turn:ip:port?transport=udp',
webUsername: 'Nom d\'Utilisateur WebRTC',
webPassword: 'Mot de Passe WebRTC',
failedLoad: 'Échec du Chargement',
limit: 'Limite',
};

View File

@ -1,6 +1,7 @@
import { AlertIcon, DashboardWrapper, SettingsButton, AddButton, SettingsLayout, CreateLayout } from './Dashboard.styled';
import { Tooltip, Switch, Select, Button, Space, Modal, Input, InputNumber, List } from 'antd';
import { ExclamationCircleOutlined, SettingOutlined, UserAddOutlined, LogoutOutlined, ReloadOutlined } from '@ant-design/icons';
import { ThemeProvider } from "styled-components";
import { useDashboard } from './useDashboard.hook';
import { AccountItem } from './accountItem/AccountItem';
import { CopyButton } from './copyButton/CopyButton';
@ -18,199 +19,200 @@ export function Dashboard() {
};
return (
<DashboardWrapper>
<div className="container">
<div className="header">
<div className="label">Accounts</div>
{ state.display === 'small' && (
<>
<div className="settings">
<SettingsButton type="text" size="small" icon={<ReloadOutlined />}
onClick={() => actions.reload()}></SettingsButton>
</div>
<div className="settings">
<SettingsButton type="text" size="small" icon={<SettingOutlined />}
onClick={() => actions.setShowSettings(true)}></SettingsButton>
</div>
<div className="settings">
<SettingsButton type="text" size="small" icon={<LogoutOutlined />}
onClick={() => actions.logout()}></SettingsButton>
</div>
{ (state.configError || state.accountsError) && (
<AlertIcon>
<ExclamationCircleOutlined />
</AlertIcon>
)}
<div className="add">
<AddButton type="text" size="large" icon={<UserAddOutlined />}
loading={state.createBusy} onClick={() => actions.setCreateLink()}></AddButton>
</div>
</>
)}
{ state.display !== 'small' && (
<>
<div className="settings">
<Tooltip placement="topRight" title="Reload Accounts">
<SettingsButton type="text" size="small" icon={<ReloadOutlined />}
onClick={() => actions.reload()}></SettingsButton>
</Tooltip>
</div>
<div className="settings">
<Tooltip placement="topRight" title="Configure Server">
<SettingsButton type="text" size="small" icon={<SettingOutlined />}
onClick={() => actions.setShowSettings(true)}></SettingsButton>
</Tooltip>
</div>
<div className="settings">
<Tooltip placement="topRight" title="Logout">
<SettingsButton type="text" size="small" icon={<LogoutOutlined />}
onClick={() => actions.logout()}></SettingsButton>
</Tooltip>
</div>
{ state.configError && (
<Tooltip placement="topRight" title="failed to load accounts">
<AlertIcon className="alert">
<ExclamationCircleOutlined />
<ThemeProvider theme={state.colors}>
<DashboardWrapper>
<div className="container">
<div className="header">
<div className="label">{ state.strings.accounts }</div>
{ state.display === 'small' && (
<>
<div className="settings">
<SettingsButton type="text" size="small" icon={<ReloadOutlined />}
onClick={() => actions.reload()}></SettingsButton>
</div>
<div className="settings">
<SettingsButton type="text" size="small" icon={<SettingOutlined />}
onClick={() => actions.setShowSettings(true)}></SettingsButton>
</div>
<div className="settings">
<SettingsButton type="text" size="small" icon={<LogoutOutlined />}
onClick={() => actions.logout()}></SettingsButton>
</div>
{ (state.configError || state.accountsError) && (
<AlertIcon>
<ExclamationCircleOutlined />
</AlertIcon>
</Tooltip>
)}
{ state.accountsError && (
<Tooltip placement="topRight" title="failed to load config">
<AlertIcon className="alert">
<ExclamationCircleOutlined />
</AlertIcon>
</Tooltip>
)}
<div className="add">
<Tooltip placement="topRight" title="Create Account Link">
<AddButton type="text" size="large" icon={<UserAddOutlined />}
loading={state.createBusy} onClick={() => actions.setCreateLink()}></AddButton>
</Tooltip>
)}
<div className="add">
<AddButton type="text" size="large" icon={<UserAddOutlined />}
loading={state.createBusy} onClick={() => actions.setCreateLink()}></AddButton>
</div>
</>
)}
{ state.display !== 'small' && (
<>
<div className="settings">
<Tooltip placement="topRight" title={state.strings.reloadAccounts}>
<SettingsButton type="text" size="small" icon={<ReloadOutlined />}
onClick={() => actions.reload()}></SettingsButton>
</Tooltip>
</div>
<div className="settings">
<Tooltip placement="topRight" title={state.strings.configureServer}>
<SettingsButton type="text" size="small" icon={<SettingOutlined />}
onClick={() => actions.setShowSettings(true)}></SettingsButton>
</Tooltip>
</div>
<div className="settings">
<Tooltip placement="topRight" title={state.strings.logout}>
<SettingsButton type="text" size="small" icon={<LogoutOutlined />}
onClick={() => actions.logout()}></SettingsButton>
</Tooltip>
</div>
{ (state.configError || state.accountsError) && (
<Tooltip placement="topRight" title={state.strings.failedLoad}>
<AlertIcon className="alert">
<ExclamationCircleOutlined />
</AlertIcon>
</Tooltip>
)}
<div className="add">
<Tooltip placement="topRight" title={state.strings.createAccount}>
<AddButton type="text" size="large" icon={<UserAddOutlined />}
loading={state.createBusy} onClick={() => actions.setCreateLink()}></AddButton>
</Tooltip>
</div>
</>
)}
</div>
<div className="body">
<List
locale={{ emptyText: '' }}
itemLayout="horizontal"
dataSource={state.accounts}
loading={state.loading}
renderItem={item => (<AccountItem item={item} remove={actions.removeAccount}/>)}
/>
</div>
</div>
<Modal bodyStyle={{ borderRadius: 8, padding: 16, ...state.menuStyle }} closable={false} visible={state.showSettings}
centered width="fitContent" footer={null} onCancel={() => actions.setShowSettings(false)}>
<SettingsLayout direction="vertical">
<div className="header">{state.strings.settings}</div>
<div className="field">
<div>{ state.strings.federatedHost }</div>
<Input placeholder={state.strings.hostHint} onChange={(e) => actions.setHost(e.target.value)}
value={state.domain} />
</div>
<div className="field">
<div>{state.strings.storageLimit}</div>
<InputNumber defaultValue={0} onChange={(e) => actions.setStorage(e)}
placeholder={state.strings.storageHint} value={state.accountStorage} />
</div>
<div className="field">
<div>{state.strings.keyType}</div>
<Select labelInValue defaultValue={{ value: 'RSA4096', label: 'RSA 4096' }}
value={state.keyType} onChange={(o) => actions.setKeyType(o.value)}>
<Select.Option value="RSA2048">RSA 2048</Select.Option>
<Select.Option value="RSA4096">RSA 4096</Select.Option>
</Select>
</div>
<div className="field">
<Space className="minHeight" size="middle">
<div>{state.strings.accountCreation}</div>
<Switch onChange={(e) => actions.setEnableOpenAccess(e)} size="small"
defaultChecked={false} checked={state.enableOpenAccess} />
{ state.enableOpenAccess && (
<InputNumber defaultValue={0} onChange={(e) => actions.setOpenAccessLimit(e)}
placeholder={state.strings.limit} value={state.openAccessLimit} />
)}
</Space>
</div>
<div className="field">
<div>{state.strings.enablePush}</div>
<Switch onChange={(e) => actions.setPushSupported(e)} size="small"
defaultChecked={true} checked={state.pushSupported} />
</div>
{ state.transformSupported && (
<div className="field">
<div>{state.strings.allowUnsealed}</div>
<Switch onChange={(e) => actions.setAllowUnsealed(e)} size="small"
defaultChecked={true} checked={state.allowUnsealed} />
</div>
</>
)}
</div>
<div className="body">
<List
locale={{ emptyText: '' }}
itemLayout="horizontal"
dataSource={state.accounts}
loading={state.loading}
renderItem={item => (<AccountItem item={item} remove={actions.removeAccount}/>)}
/>
</div>
</div>
<Modal title="Settings" visible={state.showSettings} centered bodyStyle={{ padding: 16 }}
okText="Save" onOk={() => actions.setSettings()} onCancel={() => actions.setShowSettings(false)}>
<SettingsLayout direction="vertical">
<div className="field">
<div>Federated Host:&nbsp;</div>
<Input placeholder="domain:port/app" onChange={(e) => actions.setHost(e.target.value)}
value={state.domain} />
</div>
<div className="field">
<div>Storage Limit (GB) / Account:&nbsp;</div>
<InputNumber defaultValue={0} onChange={(e) => actions.setStorage(e)}
placeholder="0 for unrestricted" value={state.accountStorage} />
</div>
<div className="field">
<div>Account Key Type:&nbsp;</div>
<Select labelInValue defaultValue={{ value: 'RSA4096', label: 'RSA 4096' }}
value={state.keyType} onChange={(o) => actions.setKeyType(o.value)}>
<Select.Option value="RSA2048">RSA 2048</Select.Option>
<Select.Option value="RSA4096">RSA 4096</Select.Option>
</Select>
</div>
<div className="field">
<Space className="minHeight" size="middle">
<div>Public Account Creation:</div>
<Switch onChange={(e) => actions.setEnableOpenAccess(e)} size="small"
defaultChecked={false} checked={state.enableOpenAccess} />
{ state.enableOpenAccess && (
<InputNumber defaultValue={0} onChange={(e) => actions.setOpenAccessLimit(e)}
placeholder="Limit" value={state.openAccessLimit} />
)}
</Space>
</div>
<div className="field">
<div>Enable Push Notification:&nbsp;</div>
<Switch onChange={(e) => actions.setPushSupported(e)} size="small"
defaultChecked={true} checked={state.pushSupported} />
</div>
{ state.transformSupported && (
<div className="field">
<div>Allow Unsealed Topics:&nbsp;</div>
<Switch onChange={(e) => actions.setAllowUnsealed(e)} size="small"
defaultChecked={true} checked={state.allowUnsealed} />
</div>
)}
<div className="field label">
<span>Topic Content:</span>
</div>
<Tooltip placement="topLeft" title="Allows images to be posted and processed in topics">
<div className="field">
<div>Enable Image Queue:&nbsp;</div>
<Switch onChange={(e) => actions.setEnableImage(e)} size="small"
defaultChecked={true} checked={state.enableImage} />
</div>
</Tooltip>
<Tooltip placement="topLeft" title="Allows for audio to be posted and processed in topics">
<div className="field">
<div>Enable Audio Queue:&nbsp;</div>
<Switch onChange={(e) => actions.setEnableAudio(e)} size="small"
defaultChecked={true} checked={state.enableAudio} />
</div>
</Tooltip>
<Tooltip placement="topLeft" title="Allows videos to be posted and processed in topics">
<div className="field">
<div>Enable Video Queue:&nbsp;</div>
<Switch onChange={(e) => actions.setEnableVideo(e)} size="small"
defaultChecked={true} checked={state.enableVideo} />
</div>
</Tooltip>
<Tooltip placement="topLeft" title="Enabled audio and video calls to contacts">
)}
<div className="field label">
<div>Enable WebRTC calls:&nbsp;</div>
<Switch onChange={(e) => actions.setEnableIce(e)} size="small"
defaultChecked={false} checked={state.enableIce} />
<span>{state.strings.topicContent}</span>
</div>
</Tooltip>
<div className="field">
<div>WebRTC Server URL:&nbsp;</div>
<Input placeholder="turn:ip:port?transport=udp" onChange={(e) => actions.setIceUrl(e.target.value)}
disabled={!state.enableIce} value={state.iceUrl} />
</div>
<div className="field">
<div>WebRTC Username:&nbsp;</div>
<Input placeholder="username" onChange={(e) => actions.setIceUsername(e.target.value)}
disabled={!state.enableIce} value={state.iceUsername} />
</div>
<div className="field">
<div>WebRTC Password:&nbsp;</div>
<Input placeholder="password" onChange={(e) => actions.setIcePassword(e.target.value)}
disabled={!state.enableIce} value={state.icePassword} />
</div>
</SettingsLayout>
</Modal>
<Modal bodyStyle={{ padding: 16 }} title="Create Account" visible={state.showCreate} centered width="fitContent"
footer={[ <Button type="primary" onClick={() => actions.setShowCreate(false)}>OK</Button> ]}
onCancel={() => actions.setShowCreate(false)}>
<CreateLayout>
<div className="url">
<div className="label">Browser Link:</div>
<div className="link">{createLink()}</div>
<CopyButton onCopy={async () => await onClipboard(createLink())} />
</div>
<div className="url">
<div className="label">App Token:</div>
<div className="token">{state.createToken}</div>
<CopyButton onCopy={async () => await onClipboard(state.createToken)} />
</div>
</CreateLayout>
</Modal>
</DashboardWrapper>
<Tooltip placement="topLeft" title={state.strings.imageHint}>
<div className="field">
<div>{state.strings.enableImage}</div>
<Switch onChange={(e) => actions.setEnableImage(e)} size="small"
defaultChecked={true} checked={state.enableImage} />
</div>
</Tooltip>
<Tooltip placement="topLeft" title={state.strings.audioHint}>
<div className="field">
<div>{state.strings.enableAudio}</div>
<Switch onChange={(e) => actions.setEnableAudio(e)} size="small"
defaultChecked={true} checked={state.enableAudio} />
</div>
</Tooltip>
<Tooltip placement="topLeft" title={state.strings.videoHint}>
<div className="field">
<div>{state.strings.enableVideo}</div>
<Switch onChange={(e) => actions.setEnableVideo(e)} size="small"
defaultChecked={true} checked={state.enableVideo} />
</div>
</Tooltip>
<Tooltip placement="topLeft" title={state.strings.webHint}>
<div className="field label">
<div>{state.strings.enableWeb}</div>
<Switch onChange={(e) => actions.setEnableIce(e)} size="small"
defaultChecked={false} checked={state.enableIce} />
</div>
</Tooltip>
<div className="field">
<div>{state.strings.serverUrl}</div>
<Input placeholder={state.strings.urlHint} onChange={(e) => actions.setIceUrl(e.target.value)}
disabled={!state.enableIce} value={state.iceUrl} />
</div>
<div className="field">
<div>{state.strings.webUsername}</div>
<Input placeholder={state.strings.username} onChange={(e) => actions.setIceUsername(e.target.value)}
disabled={!state.enableIce} value={state.iceUsername} />
</div>
<div className="field">
<div>{state.strings.webPassword}</div>
<Input placeholder={state.strings.password} onChange={(e) => actions.setIcePassword(e.target.value)}
disabled={!state.enableIce} value={state.icePassword} />
</div>
<div className="control">
<Button key="back" onClick={() => actions.setShowSettings(false)}>{state.strings.cancel}</Button>
<Button key="save" type="primary" onClick={() => actions.setSettings()} loading={state.busy}>{state.strings.save}</Button>
</div>
</SettingsLayout>
</Modal>
<Modal bodyStyle={{ borderRadius: 8, padding: 16, ...state.menuStyle }} closable={false} visible={state.showCreate} centered width="fitContent"
footer={null} onCancel={() => actions.setShowCreate(false)}>
<CreateLayout>
<div className="header">{state.strings.createAccount}</div>
<div className="url">
<div className="label">{state.strings.browserLink}</div>
<div className="link">{createLink()}</div>
<CopyButton onCopy={async () => await onClipboard(createLink())} />
</div>
<div className="url">
<div className="label">{state.strings.mobileToken}</div>
<div className="token">{state.createToken}</div>
<CopyButton onCopy={async () => await onClipboard(state.createToken)} />
</div>
<div className="control">
<Button type="primary" onClick={() => actions.setShowCreate(false)}>{state.strings.ok}</Button>
</div>
</CreateLayout>
</Modal>
</DashboardWrapper>
</ThemeProvider>
);
}

View File

@ -11,6 +11,8 @@ export const DashboardWrapper = styled.div`
height: 100%;
padding-left: 8px;
padding-right: 8px;
background-color: ${props => props.theme.baseArea};
color: ${props => props.theme.hintText};
.container {
display: flex;
@ -21,21 +23,21 @@ export const DashboardWrapper = styled.div`
border-radius: 4px;
max-width: 100%;
max-height: 80%;
background-color: ${Colors.formBackground};
background-color: ${props => props.theme.itemArea};
.header {
color: #444444;
color: ${props => props.theme.hintText};
display: flex;
flex-direction: row;
font-size: 20px;
border-bottom: 1px solid #aaaaaa;
border-bottom: 1px solid ${props => props.theme.headerBorder};
}
.body {
padding-top: 8px;
min-height: 0;
overflow: auto;
border-bottom: 1px solid #aaaaaa;
border-bottom: 1px solid ${props => props.theme.headerBorder};
margin-bottom: 16px;
}
@ -86,6 +88,19 @@ export const AlertIcon = styled.div`
export const SettingsLayout = styled(Space)`
width: 100%;
.header {
display: flex;
justify-content: center;
font-size: 1.2rem;
}
.control {
display: flex;
justify-content: flex-end;
margin-top: 16px;
gap: 16px;
}
.label {
border-top: 1px solid ${Colors.divider};
padding-top: 8px;
@ -101,10 +116,24 @@ export const SettingsLayout = styled(Space)`
display: flex;
flex-direction: row;
align-items: center;
gap: 16px;
}
`;
export const CreateLayout = styled.div`
.header {
display: flex;
justify-content: center;
font-size: 1.2rem;
}
.control {
display: flex;
justify-content: flex-end;
margin-top: 16px;
gap: 16px;
}
.url {
display: flex;
flex-direction: row;
@ -113,7 +142,7 @@ export const CreateLayout = styled.div`
.label {
padding-right: 16px;
width: 112px;
width: 145px;
}
.token {

View File

@ -12,9 +12,12 @@ export function AccountItem({ item, remove }) {
const removeAccount = () => {
modal.confirm({
title: 'Are you sure you want to delete the account?',
title: <span style={state.menuStyle}>{state.strings.confirmDelete}</span>,
content: <span style={state.menuStyle}>{state.strings.areSure}</span>,
icon: <ExclamationCircleOutlined />,
bodyStyle: { padding: 16 },
bodyStyle: { borderRadius: 8, padding: 16, ...state.menuStyle },
okText: state.strings.ok,
cancelText: state.strings.cancel,
onOk() {
applyRemoveAccount();
},
@ -27,10 +30,10 @@ export function AccountItem({ item, remove }) {
await actions.remove();
}
catch(err) {
modal.error({
title: 'Failed to Remove Account',
content: 'Please try again.',
bodyStyle: { padding: 16 },
modal.error({
title: <span style={state.menuStyle}>{state.strings.operationFailed}</span>,
content: <span style={state.menuStyle}>{state.strings.tryAgain}</span>,
bodyStyle: { borderRadius: 8, padding: 16, ...state.menuStyle },
});
}
}
@ -41,9 +44,9 @@ export function AccountItem({ item, remove }) {
}
catch(err) {
modal.error({
title: 'Failed to Set Account Status',
content: 'Please try again.',
bodyStyle: { padding: 16 },
title: <span style={state.menuStyle}>{state.strings.operationFailed}</span>,
content: <span style={state.menuStyle}>{state.strings.tryAgain}</span>,
bodyStyle: { borderRadius: 8, padding: 16, ...state.menuStyle },
});
}
}
@ -54,9 +57,9 @@ export function AccountItem({ item, remove }) {
}
catch(err) {
modal.error({
title: 'Failed to Set Account Access',
content: 'Please try again.',
bodyStyle: { padding: 16 },
title: <span style={state.menuStyle}>{state.strings.operationFailed}</span>,
content: <span style={state.menuStyle}>{state.strings.tryAgain}</span>,
bodyStyle: { borderRadius: 8, padding: 16, ...state.menuStyle },
});
}
}
@ -103,43 +106,46 @@ export function AccountItem({ item, remove }) {
)}
{ state.display !== 'small' && (
<>
<Tooltip placement="topLeft" title="Account Login Link">
<Tooltip placement="topLeft" title={state.strings.accessAccount}>
<ResetButton type="text" size="large" icon={<UnlockOutlined />}
loading={state.accessBusy} onClick={setAccountAccess}></ResetButton>
</Tooltip>
{ state.disabled && (
<Tooltip placement="topLeft" title="Enable Account">
<Tooltip placement="topLeft" title={state.strings.enableAccount}>
<EnableButton type="text" size="large" icon={<CheckCircleOutlined />}
loading={state.statusBusy} onClick={() => applyAccountStatus(false)}></EnableButton>
</Tooltip>
)}
{ !state.disabled && (
<Tooltip placement="topLeft" title="Disable Account">
<Tooltip placement="topLeft" title={state.strings.disableAccount}>
<DisableButton type="text" size="large" icon={<CloseCircleOutlined />}
loading={state.statusBusy} onClick={() => applyAccountStatus(true)}></DisableButton>
</Tooltip>
)}
<Tooltip placement="topLeft" title="Delete Account">
<Tooltip placement="topLeft" title={state.strings.deleteAccount}>
<DeleteButton type="text" size="large" icon={<UserDeleteOutlined />}
loading={state.removeBusy} onClick={removeAccount}></DeleteButton>
</Tooltip>
</>
)}
</div>
<Modal title="Access Account" visible={state.showAccess} centered width="fitContent"
footer={[ <Button type="primary" onClick={() => actions.setShowAccess(false)}>OK</Button> ]}
bodyStyle={{ padding: 16 }} onCancel={() => actions.setShowAccess(false)}>
<Modal bodyStyle={{ borderRadius: 8, padding: 16, ...state.menuStyle }} closable={false} visible={state.showAccess} centered width="fitContent"
footer={null} onCancel={() => actions.setShowAccess(false)}>
<AccessLayout>
<div className="header">{ state.strings.accessAccount }</div>
<div className="url">
<div className="label">Browser Link:</div>
<div className="label">{ state.strings.browserLink }</div>
<div className="link">{accessLink()}</div>
<CopyButton onCopy={async () => await onClipboard(accessLink())} />
</div>
<div className="url">
<div className="label">App Token:</div>
<div className="label">{ state.strings.mobileToken }</div>
<div className="token">{state.accessToken}</div>
<CopyButton onCopy={async () => await onClipboard(state.accessToken)} />
</div>
<div className="control">
<Button type="primary" onClick={() => actions.setShowAccess(false)}>{state.strings.ok}</Button>
</div>
</AccessLayout>
</Modal>
</AccountItemWrapper>

View File

@ -10,11 +10,12 @@ export const AccountItemWrapper = styled.div`
padding-right: 16px;
padding-top: 2px;
padding-bottom: 2px;
border-bottom: 1px solid #eeeeee;
border-bottom: 1px solid ${props => props.theme.itemBorder};
align-items: center;
color: ${props => props.theme.mainText};
&:hover {
background-color: #eeeeee;
background-color: ${props => props.theme.hoverArea};
}
.avatar {
@ -52,7 +53,7 @@ export const AccountItemWrapper = styled.div`
}
.storage {
color: #555555;
color: ${props => props.theme.hintText};
padding-left: 8px;
}
@ -89,6 +90,18 @@ export const DeleteButton = styled(Button)`
`
export const AccessLayout = styled.div`
.control {
display: flex;
justify-content: flex-end;
margin-top: 16px;
}
.header {
display: flex;
justify-content: center;
font-size: 1.2rem;
}
.url {
display: flex;
flex-direction: row;
@ -97,7 +110,7 @@ export const AccessLayout = styled.div`
.label {
padding-right: 16px;
width: 112px;
min-width: 145px;
}
.token {

View File

@ -12,6 +12,9 @@ export function useAccountItem(item, remove) {
removeBusy: false,
accessBusy: false,
showAccess: false,
display: null,
menuStyle: {},
strings: {},
});
const app = useContext(AppContext);
@ -31,14 +34,15 @@ export function useAccountItem(item, remove) {
guid: item?.guid,
handle: item?.handle,
storage: Math.floor(item?.storageUsed > 1073741824 ? item?.storageUsed / 1073741824 : item?.storageUsed / 1048576),
storageUnit: item?.storageUsed > 1073741824 ? "GB" : "MB",
storageUnit: item?.storageUsed > 1073741824 ? state.strings.gb : state.strings.mb,
imageUrl: item?.imageSet ? getAccountImageUrl(app.state.adminToken, item?.accountId) : null,
});
}, [app.state.adminToken, item]);
}, [app.state.adminToken, item, state.strings]);
useEffect(() => {
updateState({ display: settings.state.display });
}, [settings]);
const { display, menuStyle, strings } = settings.state;
updateState({ display, menuStyle, strings });
}, [settings.state]);
const actions = {
setAccessLink: async () => {

View File

@ -6,6 +6,7 @@ import { removeAccount } from 'api/removeAccount';
import { addAccountCreate } from 'api/addAccountCreate';
import { useNavigate } from 'react-router-dom';
import { AppContext } from 'context/AppContext';
import { SettingsContext } from 'context/SettingsContext';
export function useDashboard() {
@ -33,10 +34,14 @@ export function useDashboard() {
showCreate: false,
busy: false,
accounts: [],
colors: {},
menuStyle: {},
strings: {},
});
const navigate = useNavigate();
const app = useContext(AppContext);
const settings = useContext(SettingsContext);
const updateState = (value) => {
setState((s) => ({ ...s, ...value }));
@ -53,6 +58,11 @@ export function useDashboard() {
// eslint-disable-next-line
}, [app]);
useEffect(() => {
const { strings, colors, menuStyle } = settings.state;
updateState({ strings, colors, menuStyle });
}, [settings.state]);
const actions = {
setCreateLink: async () => {
if (!state.createBusy) {

11
todo
View File

@ -1,11 +1,4 @@
access:
- dark style
- translation
- language
- color theme
- more text hints
thread:
- dark style
- translation
@ -24,7 +17,3 @@ calling:
- fullscreen
- device selection
admin:
- dark style
- translation