mirror of
https://github.com/ether/etherpad-lite.git
synced 2025-04-23 00:46:16 -04:00
Feat/admin react (#6211)
* Added vite react admin ui. * Added react i18next. * Added pads manager. * Fixed docker build. * Fixed windows build. * Fixed installOnWindows script. * Install only if path exists.
This commit is contained in:
parent
d34b964cc2
commit
db46ffb63b
112 changed files with 3327 additions and 946 deletions
172
admin/src/pages/PadPage.tsx
Normal file
172
admin/src/pages/PadPage.tsx
Normal file
|
@ -0,0 +1,172 @@
|
|||
import {Trans, useTranslation} from "react-i18next";
|
||||
import {useEffect, useMemo, useState} from "react";
|
||||
import {useStore} from "../store/store.ts";
|
||||
import {PadSearchQuery, PadSearchResult} from "../utils/PadSearch.ts";
|
||||
import {useDebounce} from "../utils/useDebounce.ts";
|
||||
import {determineSorting} from "../utils/sorting.ts";
|
||||
import * as Dialog from "@radix-ui/react-dialog";
|
||||
|
||||
export const PadPage = ()=>{
|
||||
const settingsSocket = useStore(state=>state.settingsSocket)
|
||||
const [searchParams, setSearchParams] = useState<PadSearchQuery>({
|
||||
offset: 0,
|
||||
limit: 12,
|
||||
pattern: '',
|
||||
sortBy: 'padName',
|
||||
ascending: true
|
||||
})
|
||||
const {t} = useTranslation()
|
||||
const [searchTerm, setSearchTerm] = useState<string>('')
|
||||
const pads = useStore(state=>state.pads)
|
||||
const pages = useMemo(()=>{
|
||||
if(!pads){
|
||||
return [0]
|
||||
}
|
||||
|
||||
const totalPages = Math.ceil(pads!.total / searchParams.limit)
|
||||
return Array.from({length: totalPages}, (_, i) => i+1)
|
||||
},[pads, searchParams.limit])
|
||||
const [deleteDialog, setDeleteDialog] = useState<boolean>(false)
|
||||
const [padToDelete, setPadToDelete] = useState<string>('')
|
||||
|
||||
useDebounce(()=>{
|
||||
setSearchParams({
|
||||
...searchParams,
|
||||
pattern: searchTerm
|
||||
})
|
||||
|
||||
}, 500, [searchTerm])
|
||||
|
||||
useEffect(() => {
|
||||
if(!settingsSocket){
|
||||
return
|
||||
}
|
||||
|
||||
settingsSocket.emit('padLoad', searchParams)
|
||||
|
||||
}, [settingsSocket, searchParams]);
|
||||
|
||||
useEffect(() => {
|
||||
if(!settingsSocket){
|
||||
return
|
||||
}
|
||||
|
||||
settingsSocket.on('results:padLoad', (data: PadSearchResult)=>{
|
||||
useStore.getState().setPads(data);
|
||||
})
|
||||
|
||||
|
||||
settingsSocket.on('results:deletePad', (padID: string)=>{
|
||||
const newPads = useStore.getState().pads?.results?.filter((pad)=>{
|
||||
return pad.padName !== padID
|
||||
})
|
||||
useStore.getState().setPads({
|
||||
total: useStore.getState().pads!.total-1,
|
||||
results: newPads
|
||||
})
|
||||
})
|
||||
}, [settingsSocket, pads]);
|
||||
|
||||
const deletePad = (padID: string)=>{
|
||||
settingsSocket?.emit('deletePad', padID)
|
||||
}
|
||||
|
||||
|
||||
|
||||
return <div>
|
||||
<Dialog.Root open={deleteDialog}><Dialog.Portal>
|
||||
<Dialog.Overlay className="dialog-confirm-overlay" />
|
||||
<Dialog.Content className="dialog-confirm-content">
|
||||
<div className="">
|
||||
<div className=""></div>
|
||||
<div className="">
|
||||
{t("ep_admin_pads:ep_adminpads2_confirm", {
|
||||
padID: padToDelete,
|
||||
})}
|
||||
</div>
|
||||
<div className="settings-button-bar">
|
||||
<button onClick={()=>{
|
||||
setDeleteDialog(false)
|
||||
}}>Cancel</button>
|
||||
<button onClick={()=>{
|
||||
deletePad(padToDelete)
|
||||
setDeleteDialog(false)
|
||||
}}>Ok</button>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog.Content>
|
||||
</Dialog.Portal>
|
||||
</Dialog.Root>
|
||||
<h1><Trans i18nKey="ep_admin_pads:ep_adminpads2_manage-pads"/></h1>
|
||||
<input type="text" value={searchTerm} onChange={v=>setSearchTerm(v.target.value)}
|
||||
placeholder={t('ep_admin_pads:ep_adminpads2_search-heading')}/>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th className={determineSorting(searchParams.sortBy, searchParams.ascending, 'padName')} onClick={()=>{
|
||||
setSearchParams({
|
||||
...searchParams,
|
||||
sortBy: 'padName',
|
||||
ascending: !searchParams.ascending
|
||||
})
|
||||
}}><Trans i18nKey="ep_admin_pads:ep_adminpads2_padname"/></th>
|
||||
<th className={determineSorting(searchParams.sortBy, searchParams.ascending, 'lastEdited')} onClick={()=>{
|
||||
setSearchParams({
|
||||
...searchParams,
|
||||
sortBy: 'lastEdited',
|
||||
ascending: !searchParams.ascending
|
||||
})
|
||||
}}><Trans i18nKey="ep_admin_pads:ep_adminpads2_pad-user-count"/></th>
|
||||
<th className={determineSorting(searchParams.sortBy, searchParams.ascending, 'userCount')} onClick={()=>{
|
||||
setSearchParams({
|
||||
...searchParams,
|
||||
sortBy: 'userCount',
|
||||
ascending: !searchParams.ascending
|
||||
})
|
||||
}}><Trans i18nKey="ep_admin_pads:ep_adminpads2_last-edited"/></th>
|
||||
<th className={determineSorting(searchParams.sortBy, searchParams.ascending, 'revisionNumber')} onClick={()=>{
|
||||
setSearchParams({
|
||||
...searchParams,
|
||||
sortBy: 'revisionNumber',
|
||||
ascending: !searchParams.ascending
|
||||
})
|
||||
}}>Revision number</th>
|
||||
<th><Trans i18nKey="ep_admin_pads:ep_adminpads2_action"/></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{
|
||||
pads?.results?.map((pad)=>{
|
||||
return <tr key={pad.padName}>
|
||||
<td style={{textAlign: 'center'}}>{pad.padName}</td>
|
||||
<td style={{textAlign: 'center'}}>{pad.userCount}</td>
|
||||
<td style={{textAlign: 'center'}}>{new Date(pad.lastEdited).toLocaleString()}</td>
|
||||
<td style={{textAlign: 'center'}}>{pad.revisionNumber}</td>
|
||||
<td>
|
||||
<div className="settings-button-bar">
|
||||
<button onClick={()=>{
|
||||
setPadToDelete(pad.padName)
|
||||
setDeleteDialog(true)
|
||||
}}><Trans i18nKey="ep_admin_pads:ep_adminpads2_delete.value"/></button>
|
||||
<button onClick={()=>{
|
||||
window.open(`/p/${pad.padName}`, '_blank')
|
||||
}}>view</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
})
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
<div className="settings-button-bar">
|
||||
{pages.map((page)=>{
|
||||
return <button key={page} onClick={()=>{
|
||||
setSearchParams({
|
||||
...searchParams,
|
||||
offset: (page-1)*searchParams.limit
|
||||
})
|
||||
}}>{page}</button>
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue