Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge import #1164

Merged
merged 12 commits into from
Sep 1, 2024
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/client/src/common/api/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const dbPath = `${apiEntryUrl}/db`;
/**
* HTTP request to the current DB
*/
async function getDb(filename: string): Promise<AxiosResponse<DatabaseModel>> {
export async function getDb(filename: string): Promise<AxiosResponse<DatabaseModel>> {
return axios.post(`${dbPath}/download/`, { filename });
}

Expand Down
19 changes: 19 additions & 0 deletions apps/client/src/common/utils/mergeProjects.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { DatabaseModel } from 'ontime-types';

import { getDb, patchData } from '../api/db';

export async function mergeProjects(fileName: string, mergeKeys: object) {
alex-Arc marked this conversation as resolved.
Show resolved Hide resolved
const { data } = await getDb(fileName);
const patchObject: Partial<DatabaseModel> = {};

for (const key in mergeKeys) {
if (isValidKey(key, data)) {
Object.assign(patchObject, { [key]: data[key] });
}
}
await patchData(patchObject);
}
alex-Arc marked this conversation as resolved.
Show resolved Hide resolved

function isValidKey(key: string, obj: DatabaseModel): key is keyof DatabaseModel {
return key in obj;
}
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ export default function GeneralPanelForm() {
<option value='pt'>Portuguese</option>
<option value='es'>Spanish</option>
<option value='sv'>Swedish</option>
<option value='pl'>Polish</option>
<option value='pl'>Polish</option>
alex-Arc marked this conversation as resolved.
Show resolved Hide resolved
</Select>
</Panel.ListItem>
</Panel.ListGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,26 @@ import * as Panel from '../PanelUtils';

import ProjectCreateForm from './ProjectCreateForm';
import ProjectList from './ProjectList';
import ProjectMergeForm from './ProjectMergeForm';

import style from './ProjectPanel.module.scss';

export default function ManageProjects() {
const [isCreatingProject, setIsCreatingProject] = useState(false);
const [isMergeingProject, setIsMergeingProject] = useState<string | false>(false);
alex-Arc marked this conversation as resolved.
Show resolved Hide resolved
const [error, setError] = useState('');
const [loading, setLoading] = useState<'import' | null>(null);

const fileInputRef = useRef<HTMLInputElement>(null);

const handleToggleCreate = () => {
setIsCreatingProject((prev) => !prev);
setIsMergeingProject(false);
};

const handleStartMerge = (fileName: string) => {
setIsMergeingProject(fileName);
setIsCreatingProject(false);
};

const handleSelectFile = () => {
Expand Down Expand Up @@ -49,6 +57,7 @@ export default function ManageProjects() {
};

const handleCloseForm = () => {
setIsMergeingProject(false);
setIsCreatingProject(false);
};

Expand All @@ -70,7 +79,7 @@ export default function ManageProjects() {
variant='ontime-subtle'
onClick={handleSelectFile}
size='sm'
isDisabled={Boolean(loading) || isCreatingProject}
isDisabled={Boolean(loading) || isCreatingProject || Boolean(isMergeingProject)}
isLoading={loading === 'import'}
>
Import
Expand All @@ -79,7 +88,7 @@ export default function ManageProjects() {
variant='ontime-subtle'
onClick={handleToggleCreate}
size='sm'
isDisabled={Boolean(loading) || isCreatingProject}
isDisabled={Boolean(loading) || isCreatingProject || Boolean(isMergeingProject)}
rightIcon={<IoAdd />}
>
New
Expand All @@ -89,7 +98,8 @@ export default function ManageProjects() {
{error && <Panel.Error>{error}</Panel.Error>}
<Panel.Divider />
{isCreatingProject && <ProjectCreateForm onClose={handleCloseForm} />}
<ProjectList />
{isMergeingProject && <ProjectMergeForm onClose={handleCloseForm} fileName={isMergeingProject} />}
<ProjectList onMerge={handleStartMerge} />
</Panel.Card>
</Panel.Section>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,12 @@ import ProjectListItem, { EditMode } from './ProjectListItem';

import style from './ProjectPanel.module.scss';

export default function ProjectList() {
interface ProjectListProps {
onMerge: (fileName: string) => void;
}

export default function ProjectList(props: ProjectListProps) {
const { onMerge } = props;
const { data, refetch } = useProjectList();
const { files, lastLoadedProject } = data;

Expand Down Expand Up @@ -59,6 +64,7 @@ export default function ProjectList() {
editingFilename={editingFilename}
editingMode={editingMode}
current={project.filename === lastLoadedProject}
onMerge={onMerge}
/>
))}
</tbody>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ interface ProjectListItemProps {
onRefetch: () => Promise<void>;
editingFilename: string | null;
editingMode: EditMode | null;
onMerge: (filename: string) => void;
}

export default function ProjectListItem({
Expand All @@ -39,6 +40,7 @@ export default function ProjectListItem({
onRefetch,
onSubmit,
onToggleEditMode,
onMerge,
}: ProjectListItemProps) {
const [submitError, setSubmitError] = useState<string | null>(null);
const [loading, setLoading] = useState(false);
Expand Down Expand Up @@ -134,6 +136,7 @@ export default function ProjectListItem({
onDelete={handleDelete}
onLoad={handleLoad}
isDisabled={loading}
onMerge={onMerge}
/>
</td>
</>
Expand All @@ -150,9 +153,10 @@ interface ActionMenuProps {
onChangeEditMode: (editMode: EditMode, filename: string) => void;
onDelete: (filename: string) => void;
onLoad: (filename: string) => void;
onMerge: (filename: string) => void;
}
function ActionMenu(props: ActionMenuProps) {
const { current, filename, isDisabled, onChangeEditMode, onDelete, onLoad } = props;
const { current, filename, isDisabled, onChangeEditMode, onDelete, onLoad, onMerge } = props;

const handleRename = () => {
onChangeEditMode('rename', filename);
Expand Down Expand Up @@ -185,6 +189,9 @@ function ActionMenu(props: ActionMenuProps) {
<MenuItem onClick={() => onLoad(filename)} isDisabled={current}>
Load
</MenuItem>
<MenuItem onClick={() => onMerge(filename)} isDisabled={current}>
Partial Load
</MenuItem>
<MenuItem onClick={handleRename}>Rename</MenuItem>
<MenuItem onClick={handleDuplicate}>Duplicate</MenuItem>
<MenuItem onClick={handleDownload}>Download</MenuItem>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import { useState } from 'react';
import { useForm } from 'react-hook-form';
import { Button, Switch } from '@chakra-ui/react';
import { useQueryClient } from '@tanstack/react-query';

import { PROJECT_DATA } from '../../../../common/api/constants';
import { maybeAxiosError } from '../../../../common/api/utils';
import { mergeProjects } from '../../../../common/utils/mergeProjects';
import * as Panel from '../PanelUtils';

import style from './ProjectPanel.module.scss';

interface ProjectMergeFromProps {
onClose: () => void;
fileName: string;
}

type ProjectMergeFormValues = {
project?: boolean;
rundown?: boolean;
viewSettings?: boolean;
urlPresets?: boolean;
customFields?: boolean;
osc?: boolean;
http?: boolean;
};

export default function ProjectMergeForm(props: ProjectMergeFromProps) {
const { onClose, fileName } = props;
const [error, setError] = useState<string | null>(null);
const queryClient = useQueryClient();

const {
handleSubmit,
register,
formState: { isSubmitting, isValid },
} = useForm<ProjectMergeFormValues>({
resetOptions: {
keepDirtyValues: true,
},
});

const handleSubmitCreate = async (values: ProjectMergeFormValues) => {
try {
setError(null);

await mergeProjects(fileName, values);
await queryClient.invalidateQueries({ queryKey: PROJECT_DATA });
onClose();
} catch (error) {
setError(maybeAxiosError(error));
}
};

return (
<Panel.Section as='form' onSubmit={handleSubmit(handleSubmitCreate)}>
<Panel.Title>
Partially load {`"${fileName}"`} into this project
<div className={style.createActionButtons}>
<Button onClick={onClose} variant='ontime-ghosted' size='sm' isDisabled={isSubmitting}>
Cancel
</Button>
<Button isDisabled={!isValid} type='submit' isLoading={isSubmitting} variant='ontime-filled' size='sm'>
Merge
</Button>
</div>
</Panel.Title>
{error && <Panel.Error>{error}</Panel.Error>}
<div className={style.innerColumn}>
<span className={style.toggleOption}>
<label>Project data</label>
<Switch variant='ontime' size='md' defaultChecked={false} {...register('project')} />
</span>
<span className={style.toggleOption}>
<label>Rundown</label>
<Switch variant='ontime' size='md' defaultChecked={false} {...register('rundown')} />
</span>
<span className={style.toggleOption}>
<label>View Settings</label>
<Switch variant='ontime' size='md' defaultChecked={false} {...register('viewSettings')} />
</span>
<span className={style.toggleOption}>
<label>Url Presets</label>
<Switch variant='ontime' size='md' defaultChecked={false} {...register('urlPresets')} />
</span>
<span className={style.toggleOption}>
<label>Custom Fields</label>
<Switch variant='ontime' size='md' defaultChecked={false} {...register('customFields')} />
</span>
<span className={style.toggleOption}>
<label>OSC Integration</label>
<Switch variant='ontime' size='md' defaultChecked={false} {...register('osc')} />
</span>
<span className={style.toggleOption}>
<label>HTTP Integration</label>
<Switch variant='ontime' size='md' defaultChecked={false} {...register('http')} />
</span>
</div>
</Panel.Section>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,10 @@
flex-direction: column;
gap: 1em;
}

//TODO:: better style
.toggleOption {
display: flex;
flex-direction: row;
gap: 1em;
}
alex-Arc marked this conversation as resolved.
Show resolved Hide resolved
alex-Arc marked this conversation as resolved.
Show resolved Hide resolved