Skip to content

Commit 6294086

Browse files
authored
refactor: online sync plans code refactor & minor features
* feat: new tooltip messages on sync button, alert while log out * feat: auto-save changes * refactor: edit plan page refactor & show last update date * fix: local plan update * feat: better loading screen * refactor: change client side page version name * fix: import & tooltip provider * fix: remove comments & better naming * fix: fix of createOnlinePlan abstraction * fix: fix leaky abstraction * fix: remove unnecessary export * feat: better organization in .env module wywailem env baseurl bo doslownie do niczego ona nie byla potrzebne, tylko robilismy z tego apps url wiec po prostu to dodalem do env, dzieki czemu moglem uzywac tego pliku tez po kliencie jak juz nie bylo eksportu serwerowego, do tego lepsze nazwy i mniej zmiennych w funkcjach w page.client * fix: types fixes & better logic * fix: disable button action (auto-sync implemented)
1 parent a3e262a commit 6294086

File tree

23 files changed

+662
-319
lines changed

23 files changed

+662
-319
lines changed

frontend/package-lock.json

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

frontend/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
"@radix-ui/react-popover": "^1.1.1",
2626
"@radix-ui/react-select": "^2.1.1",
2727
"@radix-ui/react-slot": "^1.1.0",
28+
"@radix-ui/react-tooltip": "^1.1.4",
2829
"@t3-oss/env-nextjs": "^0.11.0",
2930
"@tanstack/react-query": "^5.54.1",
3031
"cheerio": "^1.0.0-rc.12",

frontend/src/actions/plans.ts

Lines changed: 5 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -3,51 +3,11 @@
33
import { revalidatePath } from "next/cache";
44

55
import { auth, fetchToAdonis } from "@/lib/auth";
6-
7-
interface CreatePlanResponseType {
8-
success: boolean;
9-
message: string;
10-
schedule: {
11-
name: string;
12-
userId: number;
13-
createdAt: string;
14-
updatedAt: string;
15-
id: number;
16-
};
17-
}
18-
19-
interface PlanResponseType {
20-
name: string;
21-
userId: number;
22-
id: number;
23-
createdAt: string;
24-
updatedAt: string;
25-
courses: Array<{
26-
id: string;
27-
name: string;
28-
department: string;
29-
lecturer: string;
30-
type: string;
31-
ects: number;
32-
semester: number;
33-
groups: Array<{
34-
id: number;
35-
name: string;
36-
day: string;
37-
time: string;
38-
room: string;
39-
}>;
40-
}>;
41-
registrations: Array<{
42-
id: string;
43-
name: string;
44-
}>;
45-
}
46-
47-
interface DeletePlanResponseType {
48-
success: boolean;
49-
message: string;
50-
}
6+
import type {
7+
CreatePlanResponseType,
8+
DeletePlanResponseType,
9+
PlanResponseType,
10+
} from "@/types";
5111

5212
export const createNewPlan = async ({
5313
name,

frontend/src/app/api/login/route.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { cookies as cookiesPromise } from "next/headers";
22
import { redirect } from "next/navigation";
33

4-
import { USOS_APPS_URL } from "@/env.mjs";
4+
import { env } from "@/env.mjs";
55
import { getRequestToken } from "@/lib/auth";
66

77
export const dynamic = "force-dynamic";
@@ -39,6 +39,6 @@ export async function GET() {
3939
});
4040

4141
return redirect(
42-
`${USOS_APPS_URL}/services/oauth/authorize?oauth_token=${token.token}`,
42+
`${env.USOS_APPS_URL}/services/oauth/authorize?oauth_token=${token.token}`,
4343
);
4444
}

frontend/src/app/layout.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { Space_Grotesk } from "next/font/google";
33
import Script from "next/script";
44
import type React from "react";
55

6-
import ClientProviders from "@/components/Providers";
6+
import { ClientProviders } from "@/components/Providers";
77
import { Toaster } from "@/components/ui/sonner";
88
import { env } from "@/env.mjs";
99
import { cn } from "@/lib/utils";
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import React from "react";
2+
3+
export const OfflineAlert = () => {
4+
return (
5+
<div className="w-full rounded-md border-2 border-blue-500 bg-blue-400/30 px-4 py-2 font-semibold text-blue-600">
6+
<h1>Jesteś offline!</h1>
7+
<p className="text-xs text-blue-950">
8+
Aby zapisać plany online na koncie, zaloguj się.
9+
</p>
10+
</div>
11+
);
12+
};

frontend/src/app/plans/edit/[id]/_components/SyncErrorAlert.tsx

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,11 @@ interface PlanResponseType {
3939
export const SyncErrorAlert = ({
4040
onlinePlan,
4141
planDate,
42-
bounce = false,
4342
downloadChanges,
4443
sendChanges,
4544
}: {
4645
onlinePlan: PlanResponseType | null | undefined;
4746
planDate: Date;
48-
bounce?: boolean;
4947
downloadChanges: () => void;
5048
sendChanges: () => void;
5149
}) => {
@@ -74,7 +72,6 @@ export const SyncErrorAlert = ({
7472
<div
7573
className={cn(
7674
"flex w-full flex-col rounded-md bg-primary/10 transition-all",
77-
{ "animate-fast-bounce": bounce },
7875
)}
7976
>
8077
<div className="flex w-full items-start justify-start p-4 pb-2">

frontend/src/app/plans/edit/[id]/_components/SyncedButton.tsx

Lines changed: 77 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -7,49 +7,89 @@ import {
77
RefreshCwIcon,
88
RefreshCwOffIcon,
99
} from "lucide-react";
10-
import React from "react";
10+
import React, { useMemo } from "react";
11+
import { toast } from "sonner";
1112

1213
import { Button } from "@/components/ui/button";
14+
import {
15+
Tooltip,
16+
TooltipContent,
17+
TooltipTrigger,
18+
} from "@/components/ui/tooltip";
19+
import type { PlanState } from "@/types";
20+
21+
const LOGIC_STATUS_RESULTS = {
22+
SYNCHRONIZED: {
23+
message: "Zsynchronizowano",
24+
icon: <CloudIcon className="size-4 text-emerald-500" />,
25+
},
26+
LOCAL_ONLY: {
27+
message: "Plan dostępny tylko lokalnie",
28+
icon: <AlertTriangleIcon className="size-4 text-rose-500" />,
29+
},
30+
SYNCING: {
31+
message: "Synchronizowanie...",
32+
icon: <RefreshCwIcon className="size-4 animate-spin text-primary" />,
33+
},
34+
DIFFERENT_DATES: {
35+
message: "Twoja wersja różni się od wersji online",
36+
icon: <GitPullRequestClosed className="size-4 text-primary" />,
37+
},
38+
LOCAL_CHANGES: {
39+
message: "Masz lokalne zmiany",
40+
icon: <RefreshCwOffIcon className="size-4 text-amber-500" />,
41+
},
42+
};
1343

1444
export const SyncedButton = ({
15-
synced,
16-
onlineId,
17-
syncing,
18-
onClick,
19-
bounceAlert,
20-
equalsDates,
45+
plan,
46+
isSyncing,
47+
isEqualsDates,
2148
}: {
22-
synced: boolean;
23-
onlineId: string | null;
24-
syncing: boolean;
25-
onClick: () => Promise<number | string | true>;
26-
bounceAlert: () => void;
27-
equalsDates: boolean;
49+
plan: PlanState;
50+
isSyncing: boolean;
51+
isEqualsDates: boolean;
2852
}) => {
53+
const planStatus = useMemo(() => {
54+
if (plan.synced && isEqualsDates) {
55+
return "SYNCHRONIZED";
56+
} else if (!(plan.onlineId ?? "")) {
57+
return "LOCAL_ONLY";
58+
} else if (isSyncing) {
59+
return "SYNCING";
60+
} else if (!isEqualsDates) {
61+
return "DIFFERENT_DATES";
62+
}
63+
return "LOCAL_CHANGES";
64+
}, [plan.synced, plan.onlineId, isSyncing, isEqualsDates]);
65+
66+
const message = LOGIC_STATUS_RESULTS[planStatus].message;
67+
const icon = LOGIC_STATUS_RESULTS[planStatus].icon;
68+
2969
return (
30-
<Button
31-
size="icon"
32-
variant="outline"
33-
className="min-w-10"
34-
onClick={() => {
35-
if (!equalsDates) {
36-
bounceAlert();
37-
} else {
38-
void onClick();
39-
}
40-
}}
41-
>
42-
{synced && equalsDates ? (
43-
<CloudIcon className="size-4 text-emerald-500" />
44-
) : !(onlineId ?? "") ? (
45-
<AlertTriangleIcon className="size-4 text-rose-500" />
46-
) : syncing ? (
47-
<RefreshCwIcon className="size-4 animate-spin text-primary" />
48-
) : !equalsDates ? (
49-
<GitPullRequestClosed className="size-4 text-primary" />
50-
) : (
51-
<RefreshCwOffIcon className="size-4 text-amber-500" />
52-
)}
53-
</Button>
70+
<Tooltip>
71+
<TooltipTrigger asChild={true}>
72+
<Button
73+
size="icon"
74+
variant="outline"
75+
className="min-w-10"
76+
onClick={() => {
77+
if (!isEqualsDates) {
78+
toast.info(
79+
"Wybierz akcję z alertu powyżej, aby zsynchronizować dane tak jak chcesz",
80+
{ duration: 5000 },
81+
);
82+
} else {
83+
return;
84+
}
85+
}}
86+
>
87+
{icon}
88+
</Button>
89+
</TooltipTrigger>
90+
<TooltipContent>
91+
<p>{message}</p>
92+
</TooltipContent>
93+
</Tooltip>
5494
);
5595
};

0 commit comments

Comments
 (0)