mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-19 14:14:17 +00:00
feat(settings): add searchable navigation
This commit is contained in:
committed by
Carlos Valente
parent
bdd815678b
commit
9e42f18299
@@ -1,29 +1,21 @@
|
||||
import { ChangeEvent, useRef, useState } from 'react';
|
||||
import { IoAdd } from 'react-icons/io5';
|
||||
import { useSearchParams } from 'react-router';
|
||||
|
||||
import { uploadProjectFile } from '../../../../common/api/db';
|
||||
import { invalidateAllCaches, maybeAxiosError } from '../../../../common/api/utils';
|
||||
import Button from '../../../../common/components/buttons/Button';
|
||||
import { validateProjectFile } from '../../../../common/utils/uploadUtils';
|
||||
import * as Panel from '../../panel-utils/PanelUtils';
|
||||
import ProjectCreateForm from './ProjectCreateForm';
|
||||
import useAppSettingsNavigation from '../../useAppSettingsNavigation';
|
||||
import ProjectList from './ProjectList';
|
||||
|
||||
export default function ManageProjects() {
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
const { setLocation } = useAppSettingsNavigation();
|
||||
const [error, setError] = useState('');
|
||||
const [loading, setLoading] = useState<'import' | null>(null);
|
||||
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
const isCreatingProject = searchParams.get('new') === 'true';
|
||||
|
||||
const handleToggleCreate = () => {
|
||||
searchParams.set('new', isCreatingProject ? 'false' : 'true');
|
||||
setSearchParams(searchParams);
|
||||
};
|
||||
|
||||
const handleSelectFile = () => {
|
||||
fileInputRef.current?.click();
|
||||
};
|
||||
@@ -49,11 +41,6 @@ export default function ManageProjects() {
|
||||
setLoading(null);
|
||||
};
|
||||
|
||||
const handleCloseForm = () => {
|
||||
searchParams.delete('new');
|
||||
setSearchParams(searchParams);
|
||||
};
|
||||
|
||||
return (
|
||||
<Panel.Section>
|
||||
<input
|
||||
@@ -68,21 +55,16 @@ export default function ManageProjects() {
|
||||
<Panel.SubHeader>
|
||||
Manage projects
|
||||
<Panel.InlineElements>
|
||||
<Button
|
||||
onClick={handleSelectFile}
|
||||
disabled={Boolean(loading) || isCreatingProject}
|
||||
loading={loading === 'import'}
|
||||
>
|
||||
<Button onClick={handleSelectFile} disabled={Boolean(loading)} loading={loading === 'import'}>
|
||||
Import
|
||||
</Button>
|
||||
<Button onClick={handleToggleCreate} disabled={Boolean(loading) || isCreatingProject}>
|
||||
<Button onClick={() => setLocation('project__create')} disabled={Boolean(loading)}>
|
||||
New <IoAdd />
|
||||
</Button>
|
||||
</Panel.InlineElements>
|
||||
</Panel.SubHeader>
|
||||
{error && <Panel.Error>{error}</Panel.Error>}
|
||||
<Panel.Divider />
|
||||
{isCreatingProject && <ProjectCreateForm onClose={handleCloseForm} />}
|
||||
<ProjectList />
|
||||
</Panel.Card>
|
||||
</Panel.Section>
|
||||
|
||||
@@ -1,84 +0,0 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
|
||||
import { createProject } from '../../../../common/api/db';
|
||||
import { maybeAxiosError } from '../../../../common/api/utils';
|
||||
import Button from '../../../../common/components/buttons/Button';
|
||||
import Input from '../../../../common/components/input/input/Input';
|
||||
import { preventEscape } from '../../../../common/utils/keyEvent';
|
||||
import * as Panel from '../../panel-utils/PanelUtils';
|
||||
|
||||
import style from './ProjectPanel.module.scss';
|
||||
|
||||
interface ProjectCreateFromProps {
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
type ProjectCreateFormValues = {
|
||||
title?: string;
|
||||
description?: string;
|
||||
info?: string;
|
||||
url?: string;
|
||||
custom?: { title: string; value: string }[];
|
||||
};
|
||||
|
||||
export default function ProjectCreateForm({ onClose }: ProjectCreateFromProps) {
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const {
|
||||
handleSubmit,
|
||||
register,
|
||||
formState: { isSubmitting, isValid },
|
||||
setFocus,
|
||||
} = useForm<ProjectCreateFormValues>({
|
||||
defaultValues: { title: '' },
|
||||
values: { title: '' },
|
||||
resetOptions: {
|
||||
keepDirtyValues: true,
|
||||
},
|
||||
});
|
||||
|
||||
// set focus to first field
|
||||
useEffect(() => {
|
||||
setFocus('title');
|
||||
}, [setFocus]);
|
||||
|
||||
const handleSubmitCreate = async (values: ProjectCreateFormValues) => {
|
||||
try {
|
||||
setError(null);
|
||||
|
||||
const filename = values.title ?? 'untitled';
|
||||
|
||||
await createProject({ filename });
|
||||
|
||||
onClose();
|
||||
} catch (error) {
|
||||
setError(maybeAxiosError(error));
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Panel.Indent
|
||||
as='form'
|
||||
onSubmit={handleSubmit(handleSubmitCreate)}
|
||||
onKeyDown={(event) => preventEscape(event, onClose)}
|
||||
>
|
||||
<Panel.Title>
|
||||
Create new project
|
||||
<Panel.InlineElements>
|
||||
<Button onClick={onClose} variant='ghosted' disabled={isSubmitting}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button disabled={!isValid} type='submit' loading={isSubmitting} variant='primary'>
|
||||
Create project
|
||||
</Button>
|
||||
</Panel.InlineElements>
|
||||
</Panel.Title>
|
||||
{error && <Panel.Error>{error}</Panel.Error>}
|
||||
<Panel.Section className={style.innerColumn}>
|
||||
<Panel.Description>Project title</Panel.Description>
|
||||
<Input fluid placeholder='Your project name' {...register('title')} />
|
||||
</Panel.Section>
|
||||
</Panel.Indent>
|
||||
);
|
||||
}
|
||||
@@ -55,7 +55,7 @@
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.empty {
|
||||
.loaderBox {
|
||||
height: 300px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user