Skip to content

Commit 3b00a84

Browse files
committed
Merge branch 'main' into edit-crag
2 parents 098a6fc + 72c07ec commit 3b00a84

File tree

21 files changed

+922
-8
lines changed

21 files changed

+922
-8
lines changed

package-lock.json

Lines changed: 23 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,11 @@
1212
"gengradsys": "node ./src/scripts/generate-grading-systems.mjs"
1313
},
1414
"dependencies": {
15-
"@headlessui/react": "^2.0.3",
16-
"@headlessui/tailwindcss": "^0.2.0",
1715
"@dnd-kit/core": "^6.1.0",
1816
"@dnd-kit/modifiers": "^7.0.0",
17+
"@dnd-kit/sortable": "^8.0.0",
18+
"@headlessui/react": "^2.0.3",
19+
"@headlessui/tailwindcss": "^0.2.0",
1920
"@react-stately/slider": "^3.2.4",
2021
"@react-stately/toggle": "^3.4.3",
2122
"@tailwindcss/container-queries": "^0.1.1",

src/app/[lang]/edit/(crag)/[cragSlug]/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ async function EditCragPage({ params: { cragSlug } }: TEditCragPageProps) {
3939
},
4040
{
4141
label: "Sektorji in smeri",
42-
link: `/`, // TODO: enter link
42+
link: `/edit/${cragSlug}/sectors`,
4343
isActive: false,
4444
icon: <IconRoutes />,
4545
},
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { Crag } from "@/graphql/generated";
2+
import EditSectorsNone from "./edit-sectors/edit-sectors-none";
3+
import EditSectorsMany from "./edit-sectors/edit-sectors-many";
4+
5+
type TEditCragSectorsProps = {
6+
crag: Crag;
7+
};
8+
9+
function EditSectors({ crag }: TEditCragSectorsProps) {
10+
// Dep: crag.label is deprecated. remove it's use after api is updated and labels migrated into name
11+
const cragHasSectors =
12+
crag.sectors.length > 1 ||
13+
!!crag.sectors[0]?.name ||
14+
!!crag.sectors[0]?.label;
15+
16+
return (
17+
<div className="px-4 xs:px-8 mt-5">
18+
{cragHasSectors ? (
19+
<EditSectorsMany sectors={crag.sectors} cragId={crag.id} />
20+
) : (
21+
<EditSectorsNone />
22+
)}
23+
</div>
24+
);
25+
}
26+
27+
export default EditSectors;
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import Dialog from "@/components/ui/dialog";
2+
import { Sector } from "@/graphql/generated";
3+
import { Dispatch, SetStateAction, useState } from "react";
4+
import deleteSectorAction from "../../server-actions/delete-sector-action";
5+
import { useRouter } from "next/navigation";
6+
7+
type TDeleteSectorDialog = {
8+
isOpen: boolean;
9+
setIsOpen: Dispatch<SetStateAction<boolean>>;
10+
sector: Sector;
11+
};
12+
13+
function DeleteSectorDialog({
14+
isOpen,
15+
setIsOpen,
16+
sector,
17+
}: TDeleteSectorDialog) {
18+
const router = useRouter();
19+
20+
const [loading, setLoading] = useState(false);
21+
22+
// Dep.: sector.label is deprecated. remove after removed in BE
23+
const labelAndName =
24+
sector.label && sector.name
25+
? `${sector.label} - ${sector.name}`
26+
: sector.label || sector.name || "";
27+
28+
const handleConfirm = async () => {
29+
setLoading(true);
30+
await deleteSectorAction(sector.id);
31+
setIsOpen(false);
32+
setLoading(false);
33+
router.refresh();
34+
};
35+
36+
return (
37+
<Dialog
38+
title="Brisanje sektorja"
39+
isOpen={isOpen}
40+
setIsOpen={setIsOpen}
41+
cancel={{ label: "Prekliči", disabled: loading }}
42+
confirm={{
43+
label: "Briši",
44+
callback: handleConfirm,
45+
dontCloseOnConfirm: true,
46+
loading: loading,
47+
disabled: loading,
48+
}}
49+
>
50+
<>
51+
Ali res želiš izbrisati sektor{" "}
52+
<span className="font-medium">{labelAndName}</span> in vse smeri v njem?
53+
</>
54+
</Dialog>
55+
);
56+
}
57+
58+
export default DeleteSectorDialog;
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
"use client";
2+
3+
import Checkbox from "@/components/ui/checkbox";
4+
import IconPlus from "@/components/ui/icons/plus";
5+
import { Sector } from "@/graphql/generated";
6+
import SectorCard from "./sector-card";
7+
import { Fragment, useEffect, useState } from "react";
8+
import SectorDialog from "./sector-dialog";
9+
import DeleteSectorDialog from "./delete-sector-dialog";
10+
import {
11+
DndContext,
12+
DragEndEvent,
13+
KeyboardSensor,
14+
PointerSensor,
15+
closestCenter,
16+
useSensor,
17+
useSensors,
18+
} from "@dnd-kit/core";
19+
import {
20+
SortableContext,
21+
arrayMove,
22+
sortableKeyboardCoordinates,
23+
} from "@dnd-kit/sortable";
24+
import { restrictToParentElement } from "@dnd-kit/modifiers";
25+
import updateSectorAction from "../../server-actions/update-sector-action";
26+
import { useRouter } from "next/navigation";
27+
28+
type TEditCragSectorsManyProps = {
29+
sectors: Sector[];
30+
cragId: string;
31+
};
32+
33+
function EditSectorsMany({ sectors, cragId }: TEditCragSectorsManyProps) {
34+
const router = useRouter();
35+
const [loading, setLoading] = useState(false);
36+
37+
const [sectorDialogType, setSectorDialogType] = useState<"new" | "edit">();
38+
const [sectorDialogIsOpen, setSectorDialogIsOpen] = useState(false);
39+
const [position, setPosition] = useState(0);
40+
const [sector, setSector] = useState<Sector>(sectors[0]);
41+
const [sortedSectors, setSortedSectors] = useState(sectors);
42+
useEffect(() => {
43+
setSortedSectors(sectors);
44+
}, [sectors]);
45+
46+
const handleCragHasSectorsChange = () => {};
47+
48+
const handleAddSectorClick = (position: number) => {
49+
setSectorDialogType("new");
50+
setPosition(position);
51+
setSectorDialogIsOpen(true);
52+
};
53+
54+
const handleEditSectorClick = (sector: Sector) => {
55+
setSectorDialogType("edit");
56+
setSector(sector);
57+
setSectorDialogIsOpen(true);
58+
};
59+
60+
const [deleteSectorDialogIsOpen, setDeleteSectorDialogIsOpen] =
61+
useState(false);
62+
const handleDeleteSectorClick = (sector: Sector) => {
63+
setSector(sector);
64+
setDeleteSectorDialogIsOpen(true);
65+
};
66+
67+
const handleDragEnd = async (event: DragEndEvent) => {
68+
const { active, over } = event;
69+
70+
if (over && active.id !== over.id) {
71+
setLoading(true);
72+
73+
const droppedSectorIndex = sortedSectors.findIndex(
74+
(sector) => sector.id === active.id
75+
);
76+
const targetSectorIndex = sortedSectors.findIndex(
77+
(sector) => sector.id === over.id
78+
);
79+
80+
const newSortedSectors = arrayMove(
81+
sortedSectors,
82+
droppedSectorIndex,
83+
targetSectorIndex
84+
);
85+
86+
setSortedSectors(newSortedSectors);
87+
88+
const updatedSectorData = {
89+
id: sortedSectors[droppedSectorIndex].id,
90+
position:
91+
droppedSectorIndex > targetSectorIndex
92+
? sortedSectors[targetSectorIndex].position
93+
: sortedSectors[targetSectorIndex].position + 1,
94+
};
95+
96+
await updateSectorAction(updatedSectorData);
97+
router.refresh();
98+
setLoading(false);
99+
}
100+
};
101+
102+
const sensors = useSensors(
103+
useSensor(PointerSensor),
104+
useSensor(KeyboardSensor, {
105+
coordinateGetter: sortableKeyboardCoordinates,
106+
})
107+
);
108+
109+
return (
110+
<>
111+
<Checkbox
112+
label="Plezališče ima več sektorjev"
113+
checked={true}
114+
disabled={loading}
115+
onChange={handleCragHasSectorsChange}
116+
/>
117+
118+
<div className="h-18 flex items-stretch mt-5">
119+
<button
120+
disabled={loading}
121+
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"}`}
122+
onClick={() => handleAddSectorClick(0)}
123+
>
124+
<span className="mr-2">dodaj sektor na začetek</span>
125+
<IconPlus />
126+
</button>
127+
</div>
128+
129+
<DndContext
130+
onDragEnd={handleDragEnd}
131+
collisionDetection={closestCenter}
132+
modifiers={[restrictToParentElement]}
133+
sensors={sensors}
134+
id="sort-sectors-dnd-context-id"
135+
>
136+
<SortableContext items={sortedSectors}>
137+
<div>
138+
{/* Dep: sector.label is deprecated. remove after api migrates it to name */}
139+
{sortedSectors.map((sector) => (
140+
<Fragment key={sector.id}>
141+
<SectorCard
142+
id={sector.id}
143+
name={`${sector.label} - ${sector.name}`}
144+
disabled={loading}
145+
onEditClick={() => handleEditSectorClick(sector)}
146+
onAddClick={() => handleAddSectorClick(sector.position + 1)}
147+
onDeleteClick={() => handleDeleteSectorClick(sector)}
148+
/>
149+
</Fragment>
150+
))}
151+
</div>
152+
</SortableContext>
153+
</DndContext>
154+
155+
{sectorDialogType === "new" ? (
156+
<SectorDialog
157+
formType="new"
158+
isOpen={sectorDialogIsOpen}
159+
setIsOpen={setSectorDialogIsOpen}
160+
position={position}
161+
cragId={cragId}
162+
/>
163+
) : (
164+
<SectorDialog
165+
formType="edit"
166+
isOpen={sectorDialogIsOpen}
167+
setIsOpen={setSectorDialogIsOpen}
168+
sector={sector}
169+
/>
170+
)}
171+
172+
<DeleteSectorDialog
173+
isOpen={deleteSectorDialogIsOpen}
174+
setIsOpen={setDeleteSectorDialogIsOpen}
175+
sector={sector}
176+
/>
177+
</>
178+
);
179+
}
180+
181+
export default EditSectorsMany;
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
"use client";
2+
3+
import Button from "@/components/ui/button";
4+
import Checkbox from "@/components/ui/checkbox";
5+
import IconRoutes from "@/components/ui/icons/routes";
6+
7+
function EditSectorsNone() {
8+
const handleCragHasSectorsChange = () => {};
9+
const handleEditRoutesButtonClick = () => {};
10+
11+
return (
12+
<div className="w-full flex justify-between flex-wrap gap-4">
13+
<Checkbox
14+
label="Plezališče ima več sektorjev"
15+
checked={false}
16+
onChange={handleCragHasSectorsChange}
17+
/>
18+
19+
<Button variant="quaternary" onClick={handleEditRoutesButtonClick}>
20+
<span className="flex">
21+
<IconRoutes />
22+
<span className="ml-2">Uredi smeri</span>
23+
</span>
24+
</Button>
25+
</div>
26+
);
27+
}
28+
29+
export default EditSectorsNone;

0 commit comments

Comments
 (0)