Skip to content

Commit d987628

Browse files
committed
more work
1 parent 174edf1 commit d987628

File tree

13 files changed

+504
-479
lines changed

13 files changed

+504
-479
lines changed

app/(cibus)/VoucherCard.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ export const VoucherCardItem = ({
8888
>
8989
<div className="flex w-full flex-row justify-between">
9090
<div>
91-
<p className="text-2xl">{voucher.amount}</p>
91+
<p className="text-2xl">{voucher.amount}</p>
9292
<p className="text-sm text-gray-500">
9393
{moment(voucher.date).format("DD/MM/YYYY")}
9494
</p>

app/(cibus)/VouchersList.tsx

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
"use client";
2+
3+
import { useAction, usePaginatedQuery, useQuery } from "convex/react";
4+
import { AnimatePresence, motion } from "framer-motion";
5+
import { RefreshCcwIcon } from "lucide-react";
6+
import moment from "moment";
7+
import { useState } from "react";
8+
import InfiniteScroll from "react-infinite-scroller";
9+
import { toast } from "sonner";
10+
import { useLocalStorage } from "usehooks-ts";
11+
12+
import { Loader } from "@/components/Loader";
13+
import { PageContainer } from "@/components/PageContainer";
14+
import { Button } from "@/components/ui/button";
15+
import {
16+
Select,
17+
SelectContent,
18+
SelectGroup,
19+
SelectItem,
20+
SelectTrigger,
21+
SelectValue,
22+
} from "@/components/ui/select";
23+
import { api } from "@/convex/_generated/api";
24+
import type { Id } from "@/convex/_generated/dataModel";
25+
import { cn } from "@/lib/utils";
26+
import type { VouchersFilters } from "@/types/vouchers-types";
27+
28+
import { VoucherCardItem } from "./VoucherCard";
29+
import { CiCircleInfo } from "react-icons/ci";
30+
31+
/**
32+
* Represents a page component for managing vouchers.
33+
*
34+
* @returns The rendered JSX element.
35+
*/
36+
export const VouchersList = () => {
37+
const [isUpdating, setIsUpdating] = useState<boolean>(false);
38+
const [filter, setFilter] = useLocalStorage<VouchersFilters>(
39+
"vouchers-list-filter",
40+
"all",
41+
);
42+
43+
const {
44+
results: vouchers,
45+
status,
46+
loadMore,
47+
} = usePaginatedQuery(
48+
api.cibus.cibusQueries.allVouchers,
49+
{ filter: filter },
50+
{ initialNumItems: 20 },
51+
);
52+
53+
const summary = useQuery(api.cibus.cibusQueries.allVouchersAggregated);
54+
55+
const [collapsed, setCollapsed] = useState<Id<"cibusVouchers"> | null>(null);
56+
const updateCibusVouchers = useAction(
57+
api.cibus.cibusActions.updateCibusVouchers,
58+
);
59+
60+
// Get the last date of the vouchers or 1 month ago if no vouchers exist
61+
const lastDate =
62+
vouchers?.[0]?.date || moment().subtract(1, "month").toDate();
63+
const lastDateFormatted = moment(lastDate).format("YYYY-MM-DD");
64+
65+
const refresh = async () => {
66+
try {
67+
setIsUpdating(true);
68+
69+
await updateCibusVouchers({
70+
fromDate: lastDateFormatted,
71+
});
72+
toast.success("השוברים עודכנו בהצלחה");
73+
} catch (error) {
74+
console.error(error);
75+
toast.error("אירעה שגיאה בעדכון השוברים", {
76+
action: {
77+
label: "נסה שוב",
78+
onClick: refresh,
79+
},
80+
description: "אם הבעיה חוזרת נא לנסות להתחבר מחדש",
81+
});
82+
} finally {
83+
setIsUpdating(false);
84+
}
85+
};
86+
87+
const handleChangeFilter = (filter: VouchersFilters) => {
88+
setFilter(filter);
89+
};
90+
91+
if (status === "LoadingFirstPage") {
92+
return (
93+
<div className="w-full h-full absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 backdrop-blur-[2px] rounded-xl p-4">
94+
<Loader />
95+
</div>
96+
);
97+
}
98+
99+
return (
100+
<PageContainer className="pb-16 px-4">
101+
<header className="mt-2 flex w-full items-center justify-between gap-3">
102+
<Button
103+
disabled={isUpdating || !vouchers}
104+
type="button"
105+
className="flex w-full gap-5 rounded-3xl bg-gradient-to-b from-pink-800 to-pink-600"
106+
onClick={() => refresh()}
107+
>
108+
{isUpdating ? "סורק..." : "משוך שוברים"}
109+
<RefreshCcwIcon
110+
className={cn("size-5", isUpdating && "animate-spin")}
111+
/>
112+
</Button>
113+
114+
<Select value={filter} onValueChange={handleChangeFilter}>
115+
<SelectTrigger
116+
className="border-pink-700 rounded-3xl text-pink-700 focus:ring-0"
117+
dir="rtl"
118+
>
119+
<SelectValue />
120+
</SelectTrigger>
121+
<SelectContent dir="rtl">
122+
<SelectGroup>
123+
<SelectItem value="all">כל השוברים</SelectItem>
124+
<SelectItem value="unused">שוברים שלא נוצלו</SelectItem>
125+
<SelectItem value="used">שוברים שנוצלו</SelectItem>
126+
<SelectItem value="bugged">שוברים תקולים</SelectItem>
127+
</SelectGroup>
128+
</SelectContent>
129+
</Select>
130+
</header>
131+
132+
133+
{/* Total unused amount */}
134+
<section
135+
className="relative mt-3 p-[8px] rounded flex h-10 w-full items-center justify-center bg-pink-500"
136+
style={{
137+
backgroundImage:
138+
"url(data:image/gif;base64,R0lGODdhWAICAMIFAM0Mg9ojd+g3bPNKYP9cWv///////////ywAAAAAWAICAAADVwi63P4wykmrvSHrzbv/YCiOZGkKaKqubOu+cCzPdD3ceK7vfO//wKBwSCQYj8ikcslsOp/QqHR6qVqv2CzGxO16v+BSbUwum882onrNbruH07h8Tq9PEwA7)",
139+
}}
140+
>
141+
<p className="p-6 text-md text-pink-900 bg-white w-full h-full rounded text-center flex items-center justify-center shadow">
142+
<CiCircleInfo className="absolute right-3 top-2 size-6"/>
143+
144+
יש לך ₪ {Math.floor(summary?.totalUnusedAmount || 0)} ב-{" "}
145+
{summary?.totalUnusedCount} שוברים שלא נוצלו
146+
</p>
147+
</section>
148+
149+
{vouchers?.length === 0 && (
150+
<p className="flex h-full flex-col justify-center text-3xl">
151+
אין שוברים להצגה
152+
</p>
153+
)}
154+
155+
{/* Vouchers cards */}
156+
<InfiniteScroll
157+
pageStart={0}
158+
loadMore={loadMore}
159+
hasMore={status === "CanLoadMore"}
160+
className="flex flex-1 flex-col gap-4 mt-4"
161+
loader={
162+
<div className="w-full flex justify-center" key={0}>
163+
<Loader />
164+
</div>
165+
}
166+
>
167+
<AnimatePresence initial={false}>
168+
{vouchers.map((voucher) => (
169+
<motion.div
170+
key={voucher._id}
171+
initial={{ height: 0, scale: 0 }}
172+
animate={{ height: "auto", scale: 1 }}
173+
exit={{ height: 0, scale: 0 }}
174+
style={{ overflow: "hidden" }}
175+
>
176+
<VoucherCardItem
177+
key={voucher._id}
178+
voucher={voucher}
179+
isCollapsed={collapsed === voucher._id}
180+
setCollapsed={setCollapsed}
181+
/>
182+
</motion.div>
183+
))}
184+
</AnimatePresence>
185+
</InfiniteScroll>
186+
187+
</PageContainer>
188+
);
189+
};

app/(cibus)/page.tsx

Lines changed: 15 additions & 170 deletions
Original file line numberDiff line numberDiff line change
@@ -1,184 +1,29 @@
11
"use client";
22

3-
import { useAction, usePaginatedQuery, useQuery } from "convex/react";
4-
import { AnimatePresence, motion } from "framer-motion";
5-
import { RefreshCcwIcon } from "lucide-react";
6-
import moment from "moment";
7-
import { useState } from "react";
8-
import InfiniteScroll from "react-infinite-scroller";
9-
import { toast } from "sonner";
10-
import { useLocalStorage } from "usehooks-ts";
3+
import { useAuth } from "@clerk/nextjs";
114

12-
import { Loader } from "@/components/Loader";
13-
import { Button } from "@/components/ui/button";
14-
import {
15-
Select,
16-
SelectContent,
17-
SelectGroup,
18-
SelectItem,
19-
SelectTrigger,
20-
SelectValue,
21-
} from "@/components/ui/select";
22-
import { api } from "@/convex/_generated/api";
23-
import type { Id } from "@/convex/_generated/dataModel";
24-
import { cn } from "@/lib/utils";
25-
import type { VouchersFilters } from "@/types/vouchers-types";
5+
import { VouchersList } from "./VouchersList";
266

27-
import { VoucherCardItem } from "./VoucherCard";
28-
import { PageContainer } from "@/components/PageContainer";
29-
30-
/**
31-
* Represents a page component for managing vouchers.
32-
*
33-
* @returns The rendered JSX element.
34-
*/
357
export default function Page() {
36-
const [isUpdating, setIsUpdating] = useState<boolean>(false);
37-
const [filter, setFilter] = useLocalStorage<VouchersFilters>(
38-
"vouchers-list-filter",
39-
"all",
40-
);
41-
42-
const {
43-
results: vouchers,
44-
status,
45-
loadMore,
46-
} = usePaginatedQuery(
47-
api.cibus.cibusQueries.allVouchers,
48-
{ filter: filter },
49-
{ initialNumItems: 20 },
50-
);
51-
52-
const summary = useQuery(api.cibus.cibusQueries.allVouchersAggragated);
53-
54-
const [collapsed, setCollapsed] = useState<Id<"cibusVouchers"> | null>(null);
55-
const updateCibusVouchers = useAction(
56-
api.cibus.cibusActions.updateCibusVouchers,
57-
);
58-
59-
// Get the last date of the vouchers or 1 month ago if no vouchers exist
60-
const lastDate =
61-
vouchers?.[0]?.date || moment().subtract(1, "month").toDate();
62-
const lastDateFormatted = moment(lastDate).format("YYYY-MM-DD");
63-
64-
const refresh = async () => {
65-
try {
66-
setIsUpdating(true);
67-
68-
await updateCibusVouchers({
69-
fromDate: lastDateFormatted,
70-
});
71-
toast.success("השוברים עודכנו בהצלחה");
72-
} catch (error) {
73-
console.error(error);
74-
toast.error("אירעה שגיאה בעדכון השוברים", {
75-
action: {
76-
label: "נסה שוב",
77-
onClick: refresh,
78-
},
79-
description: "אם הבעיה חוזרת נא לנסות להתחבר מחדש",
80-
});
81-
} finally {
82-
setIsUpdating(false);
83-
}
84-
};
85-
86-
const handleChangeFilter = (filter: VouchersFilters) => {
87-
setFilter(filter);
88-
};
8+
const { userId, isLoaded } = useAuth();
899

90-
if (status === "LoadingFirstPage") {
10+
if (!isLoaded) {
9111
return (
92-
<div className="w-full h-full absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 backdrop-blur-[2px] rounded-xl p-4">
93-
<Loader />
12+
<div>
13+
<p className="text-center text-3xl text-pretty">טוען...</p>
9414
</div>
9515
);
9616
}
9717

98-
return (
99-
<PageContainer className="pb-16 px-4">
100-
<header className="mt-2 flex w-full items-center justify-between gap-3">
101-
<Button
102-
disabled={isUpdating || !vouchers}
103-
type="button"
104-
className="flex w-full gap-5 rounded-3xl bg-gradient-to-b from-pink-800 to-pink-600"
105-
onClick={() => refresh()}
106-
>
107-
{isUpdating ? "סורק..." : "משוך שוברים"}
108-
<RefreshCcwIcon
109-
className={cn("size-5", isUpdating && "animate-spin")}
110-
/>
111-
</Button>
112-
113-
<Select value={filter} onValueChange={handleChangeFilter}>
114-
<SelectTrigger
115-
className="border-pink-700 rounded-3xl text-pink-700 focus:ring-0"
116-
dir="rtl"
117-
>
118-
<SelectValue />
119-
</SelectTrigger>
120-
<SelectContent dir="rtl">
121-
<SelectGroup>
122-
<SelectItem value="all">כל השוברים</SelectItem>
123-
<SelectItem value="unused">שוברים שלא נוצלו</SelectItem>
124-
<SelectItem value="used">שוברים שנוצלו</SelectItem>
125-
<SelectItem value="bugged">שוברים תקולים</SelectItem>
126-
</SelectGroup>
127-
</SelectContent>
128-
</Select>
129-
</header>
130-
131-
{vouchers?.length === 0 && (
132-
<p className="flex h-full flex-col justify-center text-3xl">
133-
אין שוברים להצגה
18+
if (!userId) {
19+
return (
20+
<div>
21+
<p className="text-center text-3xl text-pretty">
22+
אנא התחבר כדי לראות את השוברים שלך.
13423
</p>
135-
)}
136-
137-
{/* Vouchers cards */}
138-
<InfiniteScroll
139-
pageStart={0}
140-
loadMore={loadMore}
141-
hasMore={status === "CanLoadMore"}
142-
className="flex flex-1 flex-col gap-4 mt-4"
143-
loader={
144-
<div className="w-full flex justify-center" key={0}>
145-
<Loader />
146-
</div>
147-
}
148-
>
149-
<AnimatePresence initial={false}>
150-
{vouchers.map((voucher) => (
151-
<motion.div
152-
key={voucher._id}
153-
initial={{ height: 0, scale: 0 }}
154-
animate={{ height: "auto", scale: 1 }}
155-
exit={{ height: 0, scale: 0 }}
156-
style={{ overflow: "hidden" }}
157-
>
158-
<VoucherCardItem
159-
key={voucher._id}
160-
voucher={voucher}
161-
isCollapsed={collapsed === voucher._id}
162-
setCollapsed={setCollapsed}
163-
/>
164-
</motion.div>
165-
))}
166-
</AnimatePresence>
167-
</InfiniteScroll>
24+
</div>
25+
);
26+
}
16827

169-
{/* Total unused amount */}
170-
<footer
171-
className="brightness-110 opacity-80 rounded-xl fixed bottom-[70px] flex h-14 w-11/12 items-center justify-center bg-pink-500"
172-
style={{
173-
backgroundImage:
174-
"url(data:image/gif;base64,R0lGODdhWAICAMIFAM0Mg9ojd+g3bPNKYP9cWv///////////ywAAAAAWAICAAADVwi63P4wykmrvSHrzbv/YCiOZGkKaKqubOu+cCzPdD3ceK7vfO//wKBwSCQYj8ikcslsOp/QqHR6qVqv2CzGxO16v+BSbUwum882onrNbruH07h8Tq9PEwA7)",
175-
}}
176-
>
177-
<p className="text-xl text-white">
178-
יש לך {Math.floor(summary?.totalUnusedAmount || 0)} ₪ ב-{" "}
179-
{summary?.totalUnusedCount} שוברים שלא נוצלו
180-
</p>
181-
</footer>
182-
</PageContainer>
183-
);
28+
return <VouchersList />;
18429
}

0 commit comments

Comments
 (0)