Compare commits
2 Commits
ec9509ef6c
...
26b0246429
Author | SHA1 | Date |
---|---|---|
Juan Di Toro | 26b0246429 | |
Juan Di Toro | c9d956e1b6 |
|
@ -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();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// })();
|
|
@ -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,
|
||||||
|
@ -292,9 +305,8 @@ const ChangePasswordForm = ({
|
||||||
const data = Object.fromEntries(new FormData(e.currentTarget).entries());
|
const data = Object.fromEntries(new FormData(e.currentTarget).entries());
|
||||||
|
|
||||||
updatePassword({
|
updatePassword({
|
||||||
password: data.newPassword.toString()
|
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"
|
||||||
|
|
Loading…
Reference in New Issue