chore: added possibility to create pads via admin menu (#7100)

This commit is contained in:
SamTV12345 2025-08-26 21:51:13 +02:00 committed by GitHub
parent 061ea19702
commit a5413d7272
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 116 additions and 5 deletions

View file

@ -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>

View file

@ -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;
}

View file

@ -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>