Compare commits

...

2 Commits

Author SHA1 Message Date
Juan Di Toro 26b0246429 Merge branch 'develop' of git.lumeweb.com:LumeWeb/portal-dashboard into develop 2024-03-18 16:07:24 +01:00
Juan Di Toro c9d956e1b6 fix: remove the backup key card from my account 2024-03-18 16:07:11 +01:00
2 changed files with 134 additions and 66 deletions

55
app/data/pinning.ts Normal file
View File

@ -0,0 +1,55 @@
interface PinningStatus {
id: string;
progress: number;
status: 'inprogress' | 'completed' | 'stale';
}
// biome-ignore lint/complexity/noStaticOnlyClass: <explanation>
class PinningProcess {
private static instances: Map<string, PinningStatus> = new Map();
static async pin(id: string): Promise<{ success: boolean; message: string }> {
if (PinningProcess.instances.has(id)) {
return { success: false, message: "ID is already being processed" };
}
const pinningStatus: PinningStatus = { id, progress: 0, status: 'inprogress' };
PinningProcess.instances.set(id, pinningStatus);
// Simulate async progress
(async () => {
for (let progress = 1; progress <= 100; progress++) {
await new Promise(resolve => setTimeout(resolve, Math.floor(Math.random() * (500 - 100 + 1)) + 100)); // Simulate time passing with random duration between 100 and 500
pinningStatus.progress = progress;
if (progress === 100) {
pinningStatus.status = 'completed';
}
}
})();
return { success: true, message: "Pinning process started" };
}
static *pollProgress(id: string): Generator<PinningStatus | null, void, unknown> {
let status = PinningProcess.instances.get(id);
while (status && status.status !== 'completed') {
yield status;
status = PinningProcess.instances.get(id);
}
yield status ?? null; // Yield the final status, could be null if ID doesn't exist
}
}
// Example usage:
// (async () => {
// const { success, message } = await PinningProcess.pin("123");
// console.log(message);
// if (success) {
// const progressGenerator = PinningProcess.pollProgress("123");
// let result = progressGenerator.next();
// while (!result.done) {
// console.log(result.value); // Log the progress
// result = progressGenerator.next();
// }
// }
// })();

View File

@ -1,12 +1,23 @@
import { getFormProps, useForm } from "@conform-to/react"; import { getFormProps, useForm } from "@conform-to/react";
import { getZodConstraint, parseWithZod } from "@conform-to/zod"; import { getZodConstraint, parseWithZod } from "@conform-to/zod";
import { BaseKey, useGetIdentity, useUpdate, useUpdatePassword } from "@refinedev/core"; import {
BaseKey,
useGetIdentity,
useUpdate,
useUpdatePassword,
} from "@refinedev/core";
import { useState } from "react"; import { useState } from "react";
import { z } from "zod"; import { z } from "zod";
import { Field } from "~/components/forms"; import { Field } from "~/components/forms";
import { GeneralLayout } from "~/components/general-layout"; import { GeneralLayout } from "~/components/general-layout";
import { AddIcon, CloudIcon, CrownIcon } from "~/components/icons"; import { AddIcon, CloudIcon, CrownIcon } from "~/components/icons";
import { ManagementCard, ManagementCardAvatar, ManagementCardContent, ManagementCardFooter, ManagementCardTitle } from "~/components/management-card"; import {
ManagementCard,
ManagementCardAvatar,
ManagementCardContent,
ManagementCardFooter,
ManagementCardTitle,
} from "~/components/management-card";
import { Button } from "~/components/ui/button"; import { Button } from "~/components/ui/button";
import { import {
Dialog, Dialog,
@ -51,10 +62,12 @@ export default function MyAccount() {
<ManagementCard> <ManagementCard>
<ManagementCardTitle>Email Address</ManagementCardTitle> <ManagementCardTitle>Email Address</ManagementCardTitle>
<ManagementCardContent className="text-ring font-semibold"> <ManagementCardContent className="text-ring font-semibold">
{identity?.email} {identity?.email}
</ManagementCardContent> </ManagementCardContent>
<ManagementCardFooter> <ManagementCardFooter>
<Button className="h-12 gap-x-2" onClick={() => setModal({ ...openModal, changeEmail: true })}> <Button
className="h-12 gap-x-2"
onClick={() => setModal({ ...openModal, changeEmail: true })}>
<AddIcon /> <AddIcon />
Change Email Address Change Email Address
</Button> </Button>
@ -63,8 +76,8 @@ export default function MyAccount() {
<ManagementCard> <ManagementCard>
<ManagementCardTitle>Account Type</ManagementCardTitle> <ManagementCardTitle>Account Type</ManagementCardTitle>
<ManagementCardContent className="text-ring font-semibold flex gap-x-2"> <ManagementCardContent className="text-ring font-semibold flex gap-x-2">
Lite Premium Account Lite Premium Account
<CrownIcon /> <CrownIcon />
</ManagementCardContent> </ManagementCardContent>
<ManagementCardFooter> <ManagementCardFooter>
<Button className="h-12 gap-x-2"> <Button className="h-12 gap-x-2">
@ -82,7 +95,9 @@ export default function MyAccount() {
<PasswordDots className="mt-6" /> <PasswordDots className="mt-6" />
</ManagementCardContent> </ManagementCardContent>
<ManagementCardFooter> <ManagementCardFooter>
<Button className="h-12 gap-x-2" onClick={() => setModal({ ...openModal, changePassword: true })}> <Button
className="h-12 gap-x-2"
onClick={() => setModal({ ...openModal, changePassword: true })}>
<AddIcon /> <AddIcon />
Change Password Change Password
</Button> </Button>
@ -91,35 +106,27 @@ export default function MyAccount() {
<ManagementCard> <ManagementCard>
<ManagementCardTitle>Two-Factor Authentication</ManagementCardTitle> <ManagementCardTitle>Two-Factor Authentication</ManagementCardTitle>
<ManagementCardContent> <ManagementCardContent>
Improve security by enabling 2FA. Improve security by enabling 2FA.
</ManagementCardContent> </ManagementCardContent>
<ManagementCardFooter> <ManagementCardFooter>
<Button className="h-12 gap-x-2" onClick={() => setModal({ ...openModal, setupTwoFactor: true })}> <Button
className="h-12 gap-x-2"
onClick={() => setModal({ ...openModal, setupTwoFactor: true })}>
<AddIcon /> <AddIcon />
Enable Two-Factor Authorization Enable Two-Factor Authorization
</Button> </Button>
</ManagementCardFooter> </ManagementCardFooter>
</ManagementCard> </ManagementCard>
<ManagementCard>
<ManagementCardTitle>Backup Key</ManagementCardTitle>
<ManagementCardContent>
Never share this code with anyone.
</ManagementCardContent>
<ManagementCardFooter>
<Button className="h-12 gap-x-2">
<AddIcon />
Export Backup Key
</Button>
</ManagementCardFooter>
</ManagementCard>
</div> </div>
<h2 className="font-bold my-8">More</h2> <h2 className="font-bold my-8">More</h2>
<div className="grid grid-cols-3 gap-x-8"> <div className="grid grid-cols-3 gap-x-8">
<ManagementCard variant="accent"> <ManagementCard variant="accent">
<ManagementCardTitle>Invite a Friend</ManagementCardTitle> <ManagementCardTitle>Invite a Friend</ManagementCardTitle>
<ManagementCardContent>Get 1 GB per friend invited for free (max 5 GB).</ManagementCardContent> <ManagementCardContent>
Get 1 GB per friend invited for free (max 5 GB).
</ManagementCardContent>
<ManagementCardFooter> <ManagementCardFooter>
<Button variant="accent" className="h-12 gap-x-2"> <Button variant="accent" className="h-12 gap-x-2">
<AddIcon /> <AddIcon />
Send Invitation Send Invitation
</Button> </Button>
@ -127,7 +134,9 @@ export default function MyAccount() {
</ManagementCard> </ManagementCard>
<ManagementCard> <ManagementCard>
<ManagementCardTitle>Read our Resources</ManagementCardTitle> <ManagementCardTitle>Read our Resources</ManagementCardTitle>
<ManagementCardContent>Navigate helpful articles or get assistance.</ManagementCardContent> <ManagementCardContent>
Navigate helpful articles or get assistance.
</ManagementCardContent>
<ManagementCardFooter> <ManagementCardFooter>
<Button className="h-12 gap-x-2"> <Button className="h-12 gap-x-2">
<AddIcon /> <AddIcon />
@ -137,7 +146,9 @@ export default function MyAccount() {
</ManagementCard> </ManagementCard>
<ManagementCard> <ManagementCard>
<ManagementCardTitle>Delete Account</ManagementCardTitle> <ManagementCardTitle>Delete Account</ManagementCardTitle>
<ManagementCardContent>Once initiated, this action cannot be undone.</ManagementCardContent> <ManagementCardContent>
Once initiated, this action cannot be undone.
</ManagementCardContent>
<ManagementCardFooter> <ManagementCardFooter>
<Button className="h-12 gap-x-2" variant="destructive"> <Button className="h-12 gap-x-2" variant="destructive">
<AddIcon /> <AddIcon />
@ -170,21 +181,22 @@ export default function MyAccount() {
); );
} }
const ChangeEmailSchema = z.object({ const ChangeEmailSchema = z
email: z.string().email(), .object({
password: z.string(), email: z.string().email(),
retypePassword: z.string(), password: z.string(),
}) retypePassword: z.string(),
.superRefine((data, ctx) => { })
if (data.password !== data.retypePassword) { .superRefine((data, ctx) => {
return ctx.addIssue({ if (data.password !== data.retypePassword) {
code: z.ZodIssueCode.custom, return ctx.addIssue({
path: ["retypePassword"], code: z.ZodIssueCode.custom,
message: "Passwords do not match", path: ["retypePassword"],
}); message: "Passwords do not match",
} });
return true; }
}); return true;
});
const ChangeEmailForm = ({ const ChangeEmailForm = ({
open, open,
@ -195,7 +207,7 @@ const ChangeEmailForm = ({
setOpen: (value: boolean) => void; setOpen: (value: boolean) => void;
currentValue: string; currentValue: string;
}) => { }) => {
const{ data: identity } = useGetIdentity<{ id: BaseKey }>(); const { data: identity } = useGetIdentity<{ id: BaseKey }>();
const { mutate: updateEmail } = useUpdate(); const { mutate: updateEmail } = useUpdate();
const [form, fields] = useForm({ const [form, fields] = useForm({
id: "login", id: "login",
@ -210,13 +222,13 @@ const ChangeEmailForm = ({
const data = Object.fromEntries(new FormData(e.currentTarget).entries()); const data = Object.fromEntries(new FormData(e.currentTarget).entries());
console.log(identity); console.log(identity);
updateEmail({ updateEmail({
resource: 'users', resource: "users",
id: identity?.id || "", id: identity?.id || "",
values: { values: {
email: data.email.toString() email: data.email.toString(),
} },
}) });
} },
}); });
return ( return (
@ -255,21 +267,22 @@ const ChangeEmailForm = ({
); );
}; };
const ChangePasswordSchema = z.object({ const ChangePasswordSchema = z
currentPassword: z.string().email(), .object({
newPassword: z.string(), currentPassword: z.string().email(),
retypePassword: z.string(), newPassword: z.string(),
}) retypePassword: z.string(),
.superRefine((data, ctx) => { })
if (data.newPassword !== data.retypePassword) { .superRefine((data, ctx) => {
return ctx.addIssue({ if (data.newPassword !== data.retypePassword) {
code: z.ZodIssueCode.custom, return ctx.addIssue({
path: ["retypePassword"], code: z.ZodIssueCode.custom,
message: "Passwords do not match", path: ["retypePassword"],
}); message: "Passwords do not match",
} });
return true; }
}); return true;
});
const ChangePasswordForm = ({ const ChangePasswordForm = ({
open, open,
@ -290,11 +303,10 @@ const ChangePasswordForm = ({
e.preventDefault(); e.preventDefault();
const data = Object.fromEntries(new FormData(e.currentTarget).entries()); const data = Object.fromEntries(new FormData(e.currentTarget).entries());
updatePassword({
password: data.newPassword.toString()
});
updatePassword({
password: data.newPassword.toString(),
});
}, },
}); });
@ -390,6 +402,7 @@ const SetupTwoFactorDialog = ({
const PasswordDots = ({ className }: { className?: string }) => { const PasswordDots = ({ className }: { className?: string }) => {
return ( return (
<svg <svg
aria-hidden="true"
width="219" width="219"
height="7" height="7"
viewBox="0 0 219 7" viewBox="0 0 219 7"
@ -414,4 +427,4 @@ const PasswordDots = ({ className }: { className?: string }) => {
<circle cx="215.5" cy="3.5" r="3.5" fill="currentColor" /> <circle cx="215.5" cy="3.5" r="3.5" fill="currentColor" />
</svg> </svg>
); );
}; };