Skip to content

Commit

Permalink
Merge branch 'main' into edit-crag
Browse files Browse the repository at this point in the history
  • Loading branch information
demshy committed Nov 24, 2024
2 parents 098a6fc + 72c07ec commit 3b00a84
Show file tree
Hide file tree
Showing 21 changed files with 922 additions and 8 deletions.
23 changes: 23 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@
"gengradsys": "node ./src/scripts/generate-grading-systems.mjs"
},
"dependencies": {
"@headlessui/react": "^2.0.3",
"@headlessui/tailwindcss": "^0.2.0",
"@dnd-kit/core": "^6.1.0",
"@dnd-kit/modifiers": "^7.0.0",
"@dnd-kit/sortable": "^8.0.0",
"@headlessui/react": "^2.0.3",
"@headlessui/tailwindcss": "^0.2.0",
"@react-stately/slider": "^3.2.4",
"@react-stately/toggle": "^3.4.3",
"@tailwindcss/container-queries": "^0.1.1",
Expand Down
2 changes: 1 addition & 1 deletion src/app/[lang]/edit/(crag)/[cragSlug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ async function EditCragPage({ params: { cragSlug } }: TEditCragPageProps) {
},
{
label: "Sektorji in smeri",
link: `/`, // TODO: enter link
link: `/edit/${cragSlug}/sectors`,
isActive: false,
icon: <IconRoutes />,
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { Crag } from "@/graphql/generated";
import EditSectorsNone from "./edit-sectors/edit-sectors-none";
import EditSectorsMany from "./edit-sectors/edit-sectors-many";

type TEditCragSectorsProps = {
crag: Crag;
};

function EditSectors({ crag }: TEditCragSectorsProps) {
// Dep: crag.label is deprecated. remove it's use after api is updated and labels migrated into name
const cragHasSectors =
crag.sectors.length > 1 ||
!!crag.sectors[0]?.name ||
!!crag.sectors[0]?.label;

return (
<div className="px-4 xs:px-8 mt-5">
{cragHasSectors ? (
<EditSectorsMany sectors={crag.sectors} cragId={crag.id} />
) : (
<EditSectorsNone />
)}
</div>
);
}

export default EditSectors;
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import Dialog from "@/components/ui/dialog";
import { Sector } from "@/graphql/generated";
import { Dispatch, SetStateAction, useState } from "react";
import deleteSectorAction from "../../server-actions/delete-sector-action";
import { useRouter } from "next/navigation";

type TDeleteSectorDialog = {
isOpen: boolean;
setIsOpen: Dispatch<SetStateAction<boolean>>;
sector: Sector;
};

function DeleteSectorDialog({
isOpen,
setIsOpen,
sector,
}: TDeleteSectorDialog) {
const router = useRouter();

const [loading, setLoading] = useState(false);

// Dep.: sector.label is deprecated. remove after removed in BE
const labelAndName =
sector.label && sector.name
? `${sector.label} - ${sector.name}`
: sector.label || sector.name || "";

const handleConfirm = async () => {
setLoading(true);
await deleteSectorAction(sector.id);
setIsOpen(false);
setLoading(false);
router.refresh();
};

return (
<Dialog
title="Brisanje sektorja"
isOpen={isOpen}
setIsOpen={setIsOpen}
cancel={{ label: "Prekliči", disabled: loading }}
confirm={{
label: "Briši",
callback: handleConfirm,
dontCloseOnConfirm: true,
loading: loading,
disabled: loading,
}}
>
<>
Ali res želiš izbrisati sektor{" "}
<span className="font-medium">{labelAndName}</span> in vse smeri v njem?
</>
</Dialog>
);
}

export default DeleteSectorDialog;
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
"use client";

import Checkbox from "@/components/ui/checkbox";
import IconPlus from "@/components/ui/icons/plus";
import { Sector } from "@/graphql/generated";
import SectorCard from "./sector-card";
import { Fragment, useEffect, useState } from "react";
import SectorDialog from "./sector-dialog";
import DeleteSectorDialog from "./delete-sector-dialog";
import {
DndContext,
DragEndEvent,
KeyboardSensor,
PointerSensor,
closestCenter,
useSensor,
useSensors,
} from "@dnd-kit/core";
import {
SortableContext,
arrayMove,
sortableKeyboardCoordinates,
} from "@dnd-kit/sortable";
import { restrictToParentElement } from "@dnd-kit/modifiers";
import updateSectorAction from "../../server-actions/update-sector-action";
import { useRouter } from "next/navigation";

type TEditCragSectorsManyProps = {
sectors: Sector[];
cragId: string;
};

function EditSectorsMany({ sectors, cragId }: TEditCragSectorsManyProps) {
const router = useRouter();
const [loading, setLoading] = useState(false);

const [sectorDialogType, setSectorDialogType] = useState<"new" | "edit">();
const [sectorDialogIsOpen, setSectorDialogIsOpen] = useState(false);
const [position, setPosition] = useState(0);
const [sector, setSector] = useState<Sector>(sectors[0]);
const [sortedSectors, setSortedSectors] = useState(sectors);
useEffect(() => {
setSortedSectors(sectors);
}, [sectors]);

const handleCragHasSectorsChange = () => {};

const handleAddSectorClick = (position: number) => {
setSectorDialogType("new");
setPosition(position);
setSectorDialogIsOpen(true);
};

const handleEditSectorClick = (sector: Sector) => {
setSectorDialogType("edit");
setSector(sector);
setSectorDialogIsOpen(true);
};

const [deleteSectorDialogIsOpen, setDeleteSectorDialogIsOpen] =
useState(false);
const handleDeleteSectorClick = (sector: Sector) => {
setSector(sector);
setDeleteSectorDialogIsOpen(true);
};

const handleDragEnd = async (event: DragEndEvent) => {
const { active, over } = event;

if (over && active.id !== over.id) {
setLoading(true);

const droppedSectorIndex = sortedSectors.findIndex(
(sector) => sector.id === active.id
);
const targetSectorIndex = sortedSectors.findIndex(
(sector) => sector.id === over.id
);

const newSortedSectors = arrayMove(
sortedSectors,
droppedSectorIndex,
targetSectorIndex
);

setSortedSectors(newSortedSectors);

const updatedSectorData = {
id: sortedSectors[droppedSectorIndex].id,
position:
droppedSectorIndex > targetSectorIndex
? sortedSectors[targetSectorIndex].position
: sortedSectors[targetSectorIndex].position + 1,
};

await updateSectorAction(updatedSectorData);
router.refresh();
setLoading(false);
}
};

const sensors = useSensors(
useSensor(PointerSensor),
useSensor(KeyboardSensor, {
coordinateGetter: sortableKeyboardCoordinates,
})
);

return (
<>
<Checkbox
label="Plezališče ima več sektorjev"
checked={true}
disabled={loading}
onChange={handleCragHasSectorsChange}
/>

<div className="h-18 flex items-stretch mt-5">
<button
disabled={loading}
className={`w-full flex justify-end items-center border border-dashed rounded-lg px-4 outline-none focus-visible:ring focus-visible:ring-blue-100 ${loading ? "text-neutral-400 border-neutral-300" : "text-neutral-500 hover:border-neutral-500 hover:text-neutral-600 active:text-neutral-700 active:border-neutral-600 border-neutral-400"}`}
onClick={() => handleAddSectorClick(0)}
>
<span className="mr-2">dodaj sektor na začetek</span>
<IconPlus />
</button>
</div>

<DndContext
onDragEnd={handleDragEnd}
collisionDetection={closestCenter}
modifiers={[restrictToParentElement]}
sensors={sensors}
id="sort-sectors-dnd-context-id"
>
<SortableContext items={sortedSectors}>
<div>
{/* Dep: sector.label is deprecated. remove after api migrates it to name */}
{sortedSectors.map((sector) => (
<Fragment key={sector.id}>
<SectorCard
id={sector.id}
name={`${sector.label} - ${sector.name}`}
disabled={loading}
onEditClick={() => handleEditSectorClick(sector)}
onAddClick={() => handleAddSectorClick(sector.position + 1)}
onDeleteClick={() => handleDeleteSectorClick(sector)}
/>
</Fragment>
))}
</div>
</SortableContext>
</DndContext>

{sectorDialogType === "new" ? (
<SectorDialog
formType="new"
isOpen={sectorDialogIsOpen}
setIsOpen={setSectorDialogIsOpen}
position={position}
cragId={cragId}
/>
) : (
<SectorDialog
formType="edit"
isOpen={sectorDialogIsOpen}
setIsOpen={setSectorDialogIsOpen}
sector={sector}
/>
)}

<DeleteSectorDialog
isOpen={deleteSectorDialogIsOpen}
setIsOpen={setDeleteSectorDialogIsOpen}
sector={sector}
/>
</>
);
}

export default EditSectorsMany;
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
"use client";

import Button from "@/components/ui/button";
import Checkbox from "@/components/ui/checkbox";
import IconRoutes from "@/components/ui/icons/routes";

function EditSectorsNone() {
const handleCragHasSectorsChange = () => {};
const handleEditRoutesButtonClick = () => {};

return (
<div className="w-full flex justify-between flex-wrap gap-4">
<Checkbox
label="Plezališče ima več sektorjev"
checked={false}
onChange={handleCragHasSectorsChange}
/>

<Button variant="quaternary" onClick={handleEditRoutesButtonClick}>
<span className="flex">
<IconRoutes />
<span className="ml-2">Uredi smeri</span>
</span>
</Button>
</div>
);
}

export default EditSectorsNone;
Loading

0 comments on commit 3b00a84

Please sign in to comment.