main
ts 111 lines 4.22 KB
Raw
1 // This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
2
3 import { createElement as h, Fragment, useEffect, useMemo, useState } from 'react';
4 import { apiCall, useApiEx, useApiList } from './api'
5 import { DataTable, fillFlexParentSx } from './DataTable'
6 import { Alert, Box } from '@mui/material'
7 import { Delete, Upload } from '@mui/icons-material'
8 import { CFG, readFile, selectFiles } from './misc'
9 import { Btn, IconBtn } from './mui'
10 import { PageProps } from './App'
11 import _ from 'lodash'
12 import { alertDialog, toast } from './dialog'
13 import { Field, SelectField } from '@hfs/mui-grid-form';
14
15 export default function LangPage({ setTitleSide }: PageProps) {
16 const { list, error, connecting, reload } = useApiList('get_langs')
17 const langs = useMemo(() => ['en', ..._.uniq(list.map(x => x.code))], [list])
18 setTitleSide(useMemo(() =>
19 h(Alert, { severity: 'info', sx: { display: { xs: 'none', sm: 'inherit' } } }, "Translation is limited to the Front-end and doesn't apply to the Admin-panel"),
20 []))
21 return h(Fragment, {},
22 h(Box, { sx: { mt: 1, maxWidth: '50em', flex: 1, ...fillFlexParentSx } },
23 h(Box, { sx: { mb: 1, display: 'flex' } },
24 h(Btn, { icon: Upload, onClick: add }, "Add"),
25 h(Box, { sx: { flex: 1 } }),
26 h(ForceLang, { langs }),
27 ),
28 h(DataTable, {
29 error,
30 loading: connecting,
31 rows: useMemo(() => _.sortBy(list, x => (x.embedded ? 2 : 1) + x.code), [list.length]), // multi-sorting is only in pro version of DataGrid
32 hideFooter: true,
33 fillFlex: true,
34 columns: [
35 {
36 field: 'code',
37 width: 110,
38 valueFormatter: (value: string | undefined) => value?.toUpperCase(),
39 },
40 {
41 field: 'version',
42 width: 120,
43 hideUnder: 'sm',
44 },
45 {
46 field: 'author',
47 flex: 1,
48 hideUnder: 'sm',
49 }
50 ],
51 actions: ({ row }) => [
52 h(IconBtn, {
53 icon: Delete,
54 title: row.embedded ? "Cannot delete (embedded)" : "Delete",
55 confirm: `Delete language code "${row.code}"?`,
56 disabled: row.embedded,
57 async onClick() {
58 await apiCall('del_lang', _.pick(row, 'code'))
59 reload()
60 toast("Deleted")
61 }
62 }),
63 ]
64 })
65 )
66 )
67
68 function add() {
69 selectFiles(async list => {
70 if (!list) return
71 const langs: any = {}
72 for (const f of list)
73 langs[f.name] = await readFile(f)
74 try {
75 await apiCall('add_langs', { langs })
76 reload()
77 toast("Loaded")
78 }
79 catch (e: any) {
80 await alertDialog(e)
81 }
82 }, { accept: '.json' })
83 }
84 }
85
86 function ForceLang({ langs }: { langs: string[] }) {
87 const K = CFG.force_lang
88 const { data, reload, loading } = useApiEx('get_config', { only: [K] })
89 const [lang, setLang] = useState()
90 useEffect(() => setLang(data?.[K] ?? lang), [data])
91 const [saving, setSaving] = useState<string>()
92
93 return h(SelectField as Field<string>, {
94 fullWidth: false,
95 size: 'small',
96 disabled: Boolean(loading) || typeof saving === 'string',
97 value: saving ?? lang,
98 async onChange(v) {
99 setSaving(v)
100 try {
101 await apiCall('set_config', { values: { [K]: v } })
102 await reload()
103 }
104 finally { setSaving(undefined) }
105 },
106 options: [
107 { label: "Respect browser language", value: '' },
108 ...langs.map(x => ({ value: x, label: "Force language: " + x }))
109 ]
110 })
111 }