mirror of
https://github.com/ether/etherpad-lite.git
synced 2026-01-23 02:35:34 +00:00
chore: added possibility to create pads via admin menu (#7100)
This commit is contained in:
parent
061ea19702
commit
a5413d7272
5 changed files with 116 additions and 5 deletions
|
|
@ -1,6 +1,7 @@
|
|||
import {FC, JSX, ReactElement} from "react";
|
||||
|
||||
export type IconButtonProps = {
|
||||
style?: React.CSSProperties,
|
||||
icon: JSX.Element,
|
||||
title: string|ReactElement,
|
||||
onClick: ()=>void,
|
||||
|
|
@ -8,8 +9,8 @@ export type IconButtonProps = {
|
|||
disabled?: boolean
|
||||
}
|
||||
|
||||
export const IconButton:FC<IconButtonProps> = ({icon,className,onClick,title, disabled})=>{
|
||||
return <button onClick={onClick} className={"icon-button "+ className} disabled={disabled}>
|
||||
export const IconButton:FC<IconButtonProps> = ({icon,className,onClick,title, disabled, style})=>{
|
||||
return <button style={style} onClick={onClick} className={"icon-button "+ className} disabled={disabled}>
|
||||
{icon}
|
||||
<span>{title}</span>
|
||||
</button>
|
||||
|
|
|
|||
|
|
@ -42,6 +42,9 @@ div.menu {
|
|||
position: fixed;
|
||||
}
|
||||
|
||||
[role="dialog"] h2 {
|
||||
color: var(--etherpad-color);
|
||||
}
|
||||
|
||||
.icon-button {
|
||||
display: flex;
|
||||
|
|
@ -54,6 +57,26 @@ div.menu {
|
|||
cursor: pointer;
|
||||
}
|
||||
|
||||
.icon-button:hover {
|
||||
background-color: #13a37c;
|
||||
}
|
||||
|
||||
|
||||
.dialog-close-button {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
color: var(--etherpad-color);
|
||||
}
|
||||
|
||||
.icon-button:active {
|
||||
background-color: #13a37c;
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
.icon-button svg {
|
||||
align-self: center;
|
||||
}
|
||||
|
|
@ -868,3 +891,7 @@ input, button, select, optgroup, textarea {
|
|||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.manage-pads-header {
|
||||
display: flex;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,8 +6,13 @@ import {useDebounce} from "../utils/useDebounce.ts";
|
|||
import {determineSorting} from "../utils/sorting.ts";
|
||||
import * as Dialog from "@radix-ui/react-dialog";
|
||||
import {IconButton} from "../components/IconButton.tsx";
|
||||
import {ChevronLeft, ChevronRight, Eye, Trash2, FileStack} from "lucide-react";
|
||||
import {ChevronLeft, ChevronRight, Eye, Trash2, FileStack, PlusIcon} from "lucide-react";
|
||||
import {SearchField} from "../components/SearchField.tsx";
|
||||
import {useForm} from "react-hook-form";
|
||||
|
||||
type PadCreateProps = {
|
||||
padName: string
|
||||
}
|
||||
|
||||
export const PadPage = ()=>{
|
||||
const settingsSocket = useStore(state=>state.settingsSocket)
|
||||
|
|
@ -25,6 +30,8 @@ export const PadPage = ()=>{
|
|||
const [deleteDialog, setDeleteDialog] = useState<boolean>(false)
|
||||
const [errorText, setErrorText] = useState<string|null>(null)
|
||||
const [padToDelete, setPadToDelete] = useState<string>('')
|
||||
const [createPadDialogOpen, setCreatePadDialogOpen] = useState<boolean>(false)
|
||||
const {register, handleSubmit} = useForm<PadCreateProps>()
|
||||
const pages = useMemo(()=>{
|
||||
if(!pads){
|
||||
return 0;
|
||||
|
|
@ -70,8 +77,33 @@ export const PadPage = ()=>{
|
|||
})
|
||||
})
|
||||
|
||||
type SettingsSocketCreateReponse = {
|
||||
error: string
|
||||
} | {
|
||||
success: string
|
||||
}
|
||||
|
||||
settingsSocket.on('results:createPad', (rep: SettingsSocketCreateReponse)=>{
|
||||
if ('error' in rep) {
|
||||
useStore.getState().setToastState({
|
||||
open: true,
|
||||
title: rep.error,
|
||||
success: false
|
||||
})
|
||||
} else {
|
||||
useStore.getState().setToastState({
|
||||
open: true,
|
||||
title: rep.success,
|
||||
success: true
|
||||
})
|
||||
setCreatePadDialogOpen(false)
|
||||
// reload pads
|
||||
settingsSocket.emit('padLoad', searchParams)
|
||||
}
|
||||
})
|
||||
|
||||
settingsSocket.on('results:cleanupPadRevisions', (data)=>{
|
||||
let newPads = useStore.getState().pads?.results ?? []
|
||||
const newPads = useStore.getState().pads?.results ?? []
|
||||
|
||||
if (data.error) {
|
||||
setErrorText(data.error)
|
||||
|
|
@ -99,6 +131,12 @@ export const PadPage = ()=>{
|
|||
settingsSocket?.emit('cleanupPadRevisions', padID)
|
||||
}
|
||||
|
||||
const onPadCreate = (data: PadCreateProps)=>{
|
||||
settingsSocket?.emit('createPad', {
|
||||
padName: data.padName
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
return <div>
|
||||
<Dialog.Root open={deleteDialog}><Dialog.Portal>
|
||||
|
|
@ -139,7 +177,32 @@ export const PadPage = ()=>{
|
|||
</Dialog.Content>
|
||||
</Dialog.Portal>
|
||||
</Dialog.Root>
|
||||
<h1><Trans i18nKey="ep_admin_pads:ep_adminpads2_manage-pads"/></h1>
|
||||
<Dialog.Root open={createPadDialogOpen}>
|
||||
<Dialog.Portal>
|
||||
<Dialog.Overlay className="dialog-confirm-overlay" />
|
||||
<Dialog.Content className="dialog-confirm-content">
|
||||
<Dialog.Title className="dialog-confirm-title"><Trans i18nKey="index.newPad"/></Dialog.Title>
|
||||
<form onSubmit={handleSubmit(onPadCreate)}>
|
||||
<button className="dialog-close-button" onClick={()=>{
|
||||
setCreatePadDialogOpen(false);
|
||||
}}>x</button>
|
||||
<div style={{display: 'grid', gap: '10px', gridTemplateColumns: 'auto auto', marginBottom: '1rem'}}>
|
||||
<label><Trans i18nKey="ep_admin_pads:ep_adminpads2_padname"/></label>
|
||||
<input {...register('padName', {
|
||||
required: true
|
||||
})}/>
|
||||
</div>
|
||||
<input type="submit" value={t('admin_settings.createPad')} className="login-button" />
|
||||
</form>
|
||||
</Dialog.Content>
|
||||
</Dialog.Portal>
|
||||
</Dialog.Root>
|
||||
<span className="manage-pads-header">
|
||||
<h1><Trans i18nKey="ep_admin_pads:ep_adminpads2_manage-pads"/></h1>
|
||||
<span style={{width: '29px', marginBottom: 'auto', marginTop: 'auto', flexGrow: 1}}><IconButton style={{float: 'right'}} icon={<PlusIcon/>} title={<Trans i18nKey="index.newPad"/>} onClick={()=>{
|
||||
setCreatePadDialogOpen(true)
|
||||
}}/></span>
|
||||
</span>
|
||||
<SearchField value={searchTerm} onChange={v=>setSearchTerm(v.target.value)} placeholder={t('ep_admin_pads:ep_adminpads2_search-heading')}/>
|
||||
<table>
|
||||
<thead>
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@
|
|||
"admin_settings.current_save.value": "Einstellungen speichern",
|
||||
"admin_settings.page-title": "Einstellungen - Etherpad",
|
||||
"index.newPad": "Neues Pad",
|
||||
"admin_settings.createPad": "Erstellen",
|
||||
"index.createOpenPad": "Pad öffnen",
|
||||
"index.openPad": "Öffne ein vorhandenes Pad mit folgendem Namen:",
|
||||
"index.recentPads": "Zuletzt bearbeitete Pads",
|
||||
|
|
|
|||
|
|
@ -251,6 +251,25 @@ exports.socketio = (hookName: string, {io}: any) => {
|
|||
}
|
||||
})
|
||||
|
||||
type PadCreationOptions = {
|
||||
padName: string,
|
||||
}
|
||||
|
||||
socket.on('createPad', async ({padName}: PadCreationOptions)=>{
|
||||
const padExists = await padManager.doesPadExists(padName);
|
||||
if (padExists) {
|
||||
socket.emit('results:createPad', {
|
||||
error: 'Pad already exists',
|
||||
});
|
||||
return;
|
||||
}
|
||||
padManager.getPad(padName);
|
||||
socket.emit('results:createPad', {
|
||||
success: `Pad created ${padName}`,
|
||||
});
|
||||
return;
|
||||
})
|
||||
|
||||
socket.on('cleanupPadRevisions', async (padId: string) => {
|
||||
if (!settings.cleanup.enabled) {
|
||||
socket.emit('results:cleanupPadRevisions', {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue