Skip to content

Commit

Permalink
feat: add changes history to tower page
Browse files Browse the repository at this point in the history
  • Loading branch information
dowl-air committed Nov 8, 2024
1 parent 53b4c89 commit 1da4140
Show file tree
Hide file tree
Showing 15 changed files with 221 additions and 70 deletions.
7 changes: 6 additions & 1 deletion actions/towers/tower.change.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,16 @@ import { revalidateTag } from "next/cache";
export const changeTower = async (changeID: string) => {
const change = await getChange(changeID);
const towerDoc = await doc(db, "towers", change.tower_id);
let newValue = change.new_value;
if (change.type === "date") {
newValue = new Date(newValue);
}
await updateDoc(towerDoc, {
[change.field]: change.new_value,
[change.field]: newValue,
modified: serverTimestamp(),
});
revalidateTag(CacheTag.Towers);
revalidateTag(CacheTag.LastChangeDate);
revalidateTag(getCacheTagSpecific(CacheTag.Tower, change.tower_id));
revalidateTag(getCacheTagSpecific(CacheTag.Tower, change.tower_id));
};
2 changes: 2 additions & 0 deletions app/(with-navbar)/[type]/[nameID]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import Buttons from "@/components/tower/top/Buttons";
import RatingFormProvider from "@/components/tower/rating/RatingProvider";
import Carousel from "@/components/tower/top/Carousel";
import { notFound } from "next/navigation";
import ChangesHistory from "@/components/tower/tiles/ChangesHistory";

async function TowerPage({ params: { nameID } }: { params: { nameID: string } }) {
const tower = await getTowerObjectByNameID(nameID);
Expand Down Expand Up @@ -45,6 +46,7 @@ async function TowerPage({ params: { nameID } }: { params: { nameID: string } })
{tower.history && <HistoryText text={tower.history} />}
<RatingFormProvider tower={tower} />
<Map lat={tower.gps.latitude} long={tower.gps.longitude} name={tower.name} />
<ChangesHistory tower={tower} />
</div>
</div>
);
Expand Down
89 changes: 89 additions & 0 deletions components/tower/tiles/ChangesHistory.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { getChangesByTower } from "@/actions/changes/change.get";
import { getUser } from "@/actions/members/members.action";
import ApprovedIcon from "@/components/tower/tiles/changesHistory/ApprovedIcon";
import NewIcon from "@/components/tower/tiles/changesHistory/NewIcon";
import RejectedIcon from "@/components/tower/tiles/changesHistory/RejectedIcon";
import { ChangeState } from "@/types/Change";
import { Tower } from "@/typings";
import { cn } from "@/utils/cn";
import { formatDate } from "@/utils/date";
import { editableParameters } from "@/utils/editableParameters";
import { formatParameterValue } from "@/utils/formatValue";

const ChangesHistory = async ({ tower }: { tower: Tower }) => {
const towerChanges = await getChangesByTower(tower.id, "", 50); //todo? implement pagination
const uniqueUserIDs = [...new Set(towerChanges.map((change) => change.user_id))];
const users = await Promise.all(uniqueUserIDs.map((id) => getUser(id)));
//todo add different types of changes (opening hours, admission...)
if (towerChanges.length === 0) return null;
return (
<div className="card shadow-xl w-full mb-5">
<div className="card-body gap-0">
<h2 className="card-title text-base sm:text-lg md:text-xl text-nowrap">Historie změn</h2>
<div className="overflow-x-auto flex flex-col gap-1">
{towerChanges.map((change, idx) => {
const parameter = editableParameters.find((param) => param.name === change.field);
const user = users.find((user) => user.id === change.user_id);
const isApproved = change.state === ChangeState.Approved;
const isRejected = change.state === ChangeState.Rejected;
const isNew = change.state === ChangeState.New;
return (
<div
key={change.id}
className={cn("flex gap-1", {
"mt-7": idx === 0,
})}
>
<time className="text-gray-400 mr-1 text-nowrap">{formatDate({ date: change.created, long: false })}</time>
<div className="mx-1">
{isApproved ? <ApprovedIcon /> : null}
{isNew ? <NewIcon /> : null}
{isRejected ? <RejectedIcon /> : null}
</div>
<div className="text-nowrap">{`${user.name} ${isApproved ? "změnil/a parametr" : "navrhl/a změnu parametru"}`}</div>
<span className="text-nowrap">
<span className="font-bold">{parameter.label}</span>.
</span>
<span className="flex gap-2 ml-2 text-nowrap">
<span className={"text-neutral-500"}>
{"[ "}
<span className={cn({ "line-through": isApproved })}>
{formatParameterValue(change.old_value, parameter.type)}
</span>
</span>
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
className="text-neutral-500"
>
<path d="M18 8L22 12L18 16" />
<path d="M2 12H22" />
</svg>
<div>
<span
className={cn("text-neutral-500", {
"text-success font-bold": isApproved,
"line-through": isRejected,
})}
>
{formatParameterValue(change.new_value, parameter.type)}
</span>
{" ]"}
</div>
</span>
</div>
);
})}
</div>
</div>
</div>
);
};

export default ChangesHistory;
2 changes: 1 addition & 1 deletion components/tower/tiles/Map.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ function Map({ lat, long, name }: MapProps) {

return (
<div
className="mx-auto w-full max-w-7xl bg-secondary h-96 sm:h-[30rem] lg:h-[34rem] rounded-t-xl overflow-hidden touch-none"
className="mx-auto w-full max-w-7xl bg-secondary h-96 sm:h-[30rem] lg:h-[34rem] rounded-xl mb-5 overflow-hidden touch-none"
ref={mapElementRef}
></div>
);
Expand Down
23 changes: 23 additions & 0 deletions components/tower/tiles/changesHistory/ApprovedIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
const ApprovedIcon = () => {
return (
<div className="tooltip" data-tip="Schváleno">
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
className="text-success"
>
<circle cx="12" cy="12" r="10" />
<path d="m9 12 2 2 4-4" />
</svg>
</div>
);
};

export default ApprovedIcon;
24 changes: 24 additions & 0 deletions components/tower/tiles/changesHistory/NewIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
const NewIcon = () => {
return (
<div className="tooltip" data-tip="Zatím nerozhodnuto">
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
className="text-info"
>
<circle cx="12" cy="12" r="10" />
<path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3" />
<path d="M12 17h.01" />
</svg>
</div>
);
};

export default NewIcon;
24 changes: 24 additions & 0 deletions components/tower/tiles/changesHistory/RejectedIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
const RejectedIcon = () => {
return (
<div className="tooltip" data-tip="Zamítnuto">
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
className="text-error"
>
<circle cx="12" cy="12" r="10" />
<path d="m15 9-6 6" />
<path d="m9 9 6 6" />
</svg>
</div>
);
};

export default RejectedIcon;
43 changes: 24 additions & 19 deletions components/tower/tiles/parameters/ParametersEditDialog.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
"use client";

import { createChange } from "@/actions/changes/change.create";
import { sendMail } from "@/actions/mail";
import Step1 from "@/components/tower/tiles/parameters/edit/Step1";
import Step2 from "@/components/tower/tiles/parameters/edit/Step2";
import Step3 from "@/components/tower/tiles/parameters/edit/Step3";
import { MailSubject } from "@/types/MailSubject";
import { Tower } from "@/typings";
import { cn } from "@/utils/cn";
import { editableParameters } from "@/utils/editableParameters";
import { createSubject } from "@/utils/mail";
import { useEffect, useState } from "react";

const ParametersEditDialog = ({ tower }: { tower: Tower }) => {
Expand Down Expand Up @@ -50,7 +53,7 @@ const ParametersEditDialog = ({ tower }: { tower: Tower }) => {
</ul>

<div className="h-60 flex flex-col items-center justify-center gap-5">
{step === 0 && <Step1 parameter={parameter} setParameter={setParameter} tower={tower} />}
{step === 0 && <Step1 setParameter={setParameter} />}
{step === 1 && <Step2 newValue={newValue} setNewValue={setNewValue} parameter={parameter} tower={tower} />}
{step === 2 && <Step3 tower={tower} parameter={parameter} newValue={newValue} />}
{step === 3 && (
Expand Down Expand Up @@ -92,7 +95,7 @@ const ParametersEditDialog = ({ tower }: { tower: Tower }) => {
className={cn("btn btn-primary min-w-24", {
hidden: step === 0,
})}
onClick={() => {
onClick={async () => {
if (step === 1) {
const check = checkNewValue(newValue);
if (check !== true) {
Expand All @@ -105,24 +108,26 @@ const ParametersEditDialog = ({ tower }: { tower: Tower }) => {
if (step === 2) {
setError(null);
setLoading(true);
const type = editableParameters.find((p) => p.name === parameter)?.type || "string";
createChange({
tower_id: tower.id,
field: parameter as keyof Tower,
type,
new_value: type === "date" ? new Date(newValue).toISOString() : newValue,
old_value: tower[parameter as keyof Tower],
})
.then(() => {
resetValues();
setStep(3);
})
.catch((e) => {
setError(e.message);
})
.finally(() => {
setLoading(false);
try {
await createChange({
tower_id: tower.id,
field: parameter as keyof Tower,
type: editableParameters.find((p) => p.name === parameter)?.type || "text",
new_value: newValue,
old_value: tower[parameter as keyof Tower],
});
await sendMail({
subject: createSubject(MailSubject.Info, "Návrh změny parametru"),
text: `Byl vytvořen návrh změny parametru ${parameter} rozhledny ${tower.name} na hodnotu ${newValue}.`,
});
} catch (e) {
setError(e.message);
setLoading(false);
return;
}
resetValues();
setStep(3);
setLoading(false);
}
if (step === 3) {
setError(null);
Expand Down
41 changes: 1 addition & 40 deletions components/tower/tiles/parameters/edit/Step1.tsx
Original file line number Diff line number Diff line change
@@ -1,49 +1,10 @@
import { Tower } from "@/typings";
import { formatDate } from "@/utils/date";
import { editableParameters } from "@/utils/editableParameters";

export const formatValue = (value: any, type: string): string => {
switch (type) {
case "date":
return formatDate({ date: value });
case "array":
return value.join(", ");
default:
return value;
}
};

const Step1 = ({
parameter,
setParameter,
tower,
}: {
parameter: keyof Tower | "default";
setParameter: (p: keyof Tower | "default") => void;
tower: Tower;
}) => {
const Step1 = ({ setParameter }: { setParameter: (p: keyof Tower | "default") => void }) => {
return (
<div className="flex justify-center w-full flex-col">
<h2 className="text-center font-bold mb-4 mt-6">Vyberte parametr</h2>
{/* <label className="form-control w-full max-w-xs">
<div className="label">
<span className="label-text">Vyberte parametr</span>
</div>
<select
value={parameter}
className="select select-bordered select-primary w-full max-w-xs mx-auto"
onChange={(e) => setParameter(e.target.value as keyof Tower)}
>
<option value="default" disabled>
Jaký parametr chcete upravit?
</option>
{editableParameters.map((param) => (
<option key={param.name} value={param.name}>
{param.label}
</option>
))}
</select>
</label> */}
<div className="grid grid-cols-2 gap-3">
{editableParameters.map((param) => (
<div key={param.name} className="btn btn-sm" onClick={() => setParameter(param.name)}>
Expand Down
4 changes: 2 additions & 2 deletions components/tower/tiles/parameters/edit/Step2.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Tower } from "@/typings";
import { editableParameters } from "@/utils/editableParameters";
import { formatValue } from "./Step1";
import { formatParameterValue } from "@/utils/formatValue";

const Step2 = ({
newValue,
Expand All @@ -20,7 +20,7 @@ const Step2 = ({
<h3 className="flex w-full justify-center text-lg font-bold">{label}</h3>
<div className="flex w-full justify-center gap-1 flex-wrap">
<p>Aktuální hodnota: </p>
<div className="font-bold">{formatValue(tower[parameter as keyof Tower], type)}</div>
<div className="font-bold">{formatParameterValue(tower[parameter as keyof Tower], type)}</div>
</div>
<div className="flex justify-center w-full">
<label className="form-control w-full max-w-xs">
Expand Down
8 changes: 5 additions & 3 deletions components/tower/tiles/parameters/edit/Step3.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { formatValue } from "@/components/tower/tiles/parameters/edit/Step1";
import { Tower } from "@/typings";
import { editableParameters } from "@/utils/editableParameters";
import { formatParameterValue } from "@/utils/formatValue";

const Step3 = ({ tower, parameter, newValue }: { tower: Tower; parameter: string; newValue: any }) => {
const { label, type } = editableParameters.find((p) => p.name === parameter) || {};
Expand All @@ -11,12 +11,14 @@ const Step3 = ({ tower, parameter, newValue }: { tower: Tower; parameter: string
<div className="flex w-full justify-center">
<div className="flex flex-col gap-1 items-center flex-1">
<p>Stará hodnota</p>
<div className="font-bold text-lg text-error line-through text-center">{formatValue(tower[parameter as keyof Tower], type)}</div>
<div className="font-bold text-lg text-error line-through text-center">
{formatParameterValue(tower[parameter as keyof Tower], type)}
</div>
</div>
<div className="divider divider-horizontal" />
<div className="flex flex-col gap-1 items-center flex-1">
<p>Nová hodnota</p>
<div className="font-bold text-lg text-success text-center">{formatValue(newValue, type)}</div>
<div className="font-bold text-lg text-success text-center">{formatParameterValue(newValue, type)}</div>
</div>
</div>
</>
Expand Down
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"compilerOptions": {
"target": "es5",
"target": "es6",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
Expand Down
Loading

0 comments on commit 1da4140

Please sign in to comment.