Skip to content

Commit

Permalink
feat(frontend): improve commit UX
Browse files Browse the repository at this point in the history
This commit changes multiple things to make manual commiting easier:

- Allows to hide pending commit drawer so multiple commits now possible
- Adds drag-n-drop support to a commit modal window so you can append existing commits
- Adds an option to remove already added packages
- Adds progress indicator for package upload
  • Loading branch information
LordTermor committed Jun 1, 2024
1 parent 1e2cd0a commit 6541b70
Show file tree
Hide file tree
Showing 11 changed files with 472 additions and 274 deletions.
30 changes: 18 additions & 12 deletions frontend/src/components/CommitCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,25 @@ import {
faTrashCan
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { useMemo } from "react";
import { Card, CardProps } from "react-daisyui";

export type ICommitCardProps = CardProps & {
commit: ICommit;
onActivate?: (commit: ICommit) => void;
onDeleteRequested?: (id: string) => void;
export type CommitCardProps = CardProps & {
section: ISection;
commit: Commit;
onActivate?: (section: ISection, commit: Commit) => void;
onDeleteRequested?: (section: ISection) => void;
};

export default (props: ICommitCardProps) => {
const section = props.commit.section;
export default (props: CommitCardProps) => {
const { branch, repository, architecture } = useMemo(
() => props.section,
[props.section]
);

return (
<Card
onClick={() => props.onActivate?.(props.commit)}
onClick={() => props.onActivate?.(props.section, props.commit)}
{...props}
className="h-30 bg-white"
>
Expand All @@ -33,22 +38,23 @@ export default (props: ICommitCardProps) => {
onClick={(e) => {
e.stopPropagation();
if (props.onDeleteRequested)
return props.onDeleteRequested(props.commit.id);
return props.onDeleteRequested(props.section);
}}
className="px-1"
icon={faTrashCan}
/>
</div>
<Card.Body className="font-bold">
{props.commit.packages.length} packages
{props.commit.size} package
{props.commit.size == 1 ? "" : "s"}
</Card.Body>
<span className="space-x-4">
<FontAwesomeIcon className="px-1" icon={faCodeBranch} />
{section.branch}
{branch}
<FontAwesomeIcon className="px-1" icon={faCubes} />
{section.repository}
{repository}
<FontAwesomeIcon className="px-1" icon={faMicrochip} />
{section.architecture}
{architecture}
</span>
</Card>
);
Expand Down
58 changes: 58 additions & 0 deletions frontend/src/components/CommitDrawer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { Button, Drawer, DrawerProps, Menu } from "react-daisyui";
import CommitCard from "./CommitCard";
import { SectionUtils } from "../utils/SectionUtils";

type CommitDrawerProps = DrawerProps & {
isOpen: boolean;
commits: Commits;
onPush: (commits: Commits) => void;
onCardActivate: (section: ISection, commit: Commit) => void;
onCardDelete: (section: ISection) => void;
};

export default (props: CommitDrawerProps) => {
return (
<Drawer
{...props}
open={props.commits.size > 0 && props.isOpen}
contentClassName="fm-content h-full"
className="h-full"
side={
<div>
<label
htmlFor="my-drawer-2"
className="drawer-overlay"
></label>
<Menu className="h-screen p-4 w-100 space-y-4 bg-accent">
<li className="text-3xl font-bold text-white">
Pending commits
</li>
{Array.from(props.commits).map(([section, commit]) => {
return (
<Menu.Item>
<CommitCard
section={SectionUtils.fromString(
section
)}
onActivate={props.onCardActivate}
commit={commit}
onDeleteRequested={props.onCardDelete}
/>
</Menu.Item>
);
})}
<div className="grow"></div>
<Button
color="ghost"
className="text-white"
onClick={(e) => props.onPush(props.commits)}
>
Push commits
</Button>
</Menu>
</div>
}
end={true}
/>
);
};
188 changes: 125 additions & 63 deletions frontend/src/components/CommitModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,104 +8,166 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Button, Form, Modal, ModalProps, Table } from "react-daisyui";
import {
faCube,
faRemove,
faSignature,
faTrashCan
} from "@fortawesome/free-solid-svg-icons";
import { toast } from "react-toastify";
import "react-toastify/dist/ReactToastify.css";
import * as uuid from "uuid";
import { forwardRef, useCallback, useEffect, useState } from "react";
import { createPortal } from "react-dom";
import SectionSelect from "./SectionSelect";
import Dropzone from "react-dropzone";
import { usePackageDropHandler } from "../hooks/DragNDropHooks";

export type CommitModalProps = ModalProps & {
isNew?: boolean;
commit?: ICommit;
commit?: Commit;
section: ISection;
sections?: ISection[];
onCommitSubmit?: (commit: ICommit) => void;
onCommitDelete?: (id: string) => void;
onCommitSubmit?: (section: ISection, commit: Commit) => void;
onCommitDelete?: (section: ISection | undefined) => void;
onPackageDrop?: (files: File[]) => void;
};

export default forwardRef<HTMLDialogElement, CommitModalProps>(
(props: CommitModalProps, ref) => {
const [commit, setCommit] = useState<ICommit>(
props.commit || { id: uuid.v4(), section: {}, packages: [] }
);
const [commit, setCommit] = useState<Commit>();
const [section, setSection] = useState<ISection>();

useEffect(
() =>
setCommit(
props.commit || { id: uuid.v4(), section: {}, packages: [] }
),
[props.commit]
);
useEffect(() => setCommit(props.commit), [props.commit]);

const { id, section, packages } = commit;
useEffect(() => setSection(props.section), [props.section]);

const { branch, repository, architecture } = section;
useEffect(() => {
if (commit?.size == 0) {
props.onCommitDelete?.(section);
}
}, [commit, props.onCommitDelete, section]);

const commitCallback = useCallback(() => {
toast.success("Commit created!");

if (props.onCommitSubmit) props.onCommitSubmit(commit);
if (props.onCommitSubmit && commit && section)
props.onCommitSubmit(section, commit);
}, [commit, props.onSubmit]);

return createPortal(
<Modal ref={ref} className="w-11/12 max-w-5xl" {...props}>
<Modal.Header className="text-3xl font-bold">
Commit
</Modal.Header>
<Modal.Body>
<Form>
<Form.Label title="Section" />
<Modal.Header>
<span>Commit</span>
<div className="float-right">
<SectionSelect
sections={props.sections || []}
selectedSection={section}
className="w-full"
onSelected={(section) => {
setCommit({ ...commit, section: section });
setSection(section);
}}
/>
<Form.Label title="Packages" />
</div>
</Modal.Header>
<Dropzone
noClick={true}
onDrop={usePackageDropHandler(section, (section, commit) =>
setCommit((prevCommit) => {
const newCommit = new Map(prevCommit);

<Table
zebra={true}
size="xs"
className="overflow-x-auto"
>
<Table.Head>
<span>Name</span>
<span />
</Table.Head>
<Table.Body>
{props.commit?.packages.map((value) => {
return (
<Table.Row>
<span className="flex items-center">
<FontAwesomeIcon
icon={faCube}
className="px-2 max-h-6"
/>
{value.signatureFile !==
undefined && (
<FontAwesomeIcon
icon={faSignature}
color="green"
className="px-2 max-h-6"
/>
)}
commit.forEach((value, key) => {
newCommit.set(key, {
...value,
...commit.get(key)
});
});

return newCommit;
})
)}
>
{({ getRootProps, getInputProps }) => (
<Modal.Body {...getRootProps()}>
<input {...getInputProps()} />
<Form>
<Form.Label title="Packages" />

{value.name}
</span>
<span></span>
</Table.Row>
);
})}
</Table.Body>
</Table>
</Form>
</Modal.Body>
<Table
zebra={true}
size="xs"
className="overflow-x-auto"
>
<Table.Head>
<span>Name</span>
<span />
<span />
</Table.Head>
<Table.Body>
{Array.from(commit || []).map(
([name, upload]) => {
return (
<Table.Row>
<span className="flex items-center">
{upload.file !==
undefined && (
<FontAwesomeIcon
icon={
faCube
}
className="px-2 max-h-6"
/>
)}
{upload.signatureFile !==
undefined && (
<FontAwesomeIcon
icon={
faSignature
}
color="green"
className="px-2 max-h-6"
/>
)}

{name}
</span>
<span></span>
<span>
<Button
size="xs"
color="error"
onClick={() => {
setCommit(
(
prevCommit
) => {
const newCommit =
new Map(
prevCommit
);
newCommit.delete(
name
);
return newCommit;
}
);
}}
>
<FontAwesomeIcon
icon={
faTrashCan
}
color="white"
className="max-h-6"
/>
</Button>
</span>
</Table.Row>
);
}
)}
</Table.Body>
</Table>
</Form>
</Modal.Body>
)}
</Dropzone>
<Form className="mt-6">
<span className="flex space-x-4 justify-end">
{props.isNew && (
Expand All @@ -118,7 +180,7 @@ export default forwardRef<HTMLDialogElement, CommitModalProps>(
}
onClick={(e) => {
if (props.onCommitDelete)
props.onCommitDelete(commit.id);
props.onCommitDelete(props.section);
}}
type="button"
color="error"
Expand Down
Loading

0 comments on commit 6541b70

Please sign in to comment.