Skip to content

Implement user purge flow #217

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 12 commits into
base: main
Choose a base branch
from
8 changes: 4 additions & 4 deletions src/playground/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src/playground/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"private": true,
"type": "module",
"dependencies": {
"@aaronpowell/react-static-web-apps-auth": "^1.6.0",
"@aaronpowell/react-static-web-apps-auth": "^1.7.2",
"@azure/openai": "^1.0.0-beta.11",
"@fluentui/react-components": "^9.34.0",
"@fluentui/react-icons": "^2.0.218",
Expand Down
32 changes: 32 additions & 0 deletions src/playground/src/adjustedLocalTime.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
export const adjustedLocalTime = (
timestamp: Date,
utcOffsetInMinutes: number
): string => {
// returns time zone adjusted date/time
const date = new Date(timestamp);
// get the timezone offset component that was added as no tz supplied in date time
const tz = date.getTimezoneOffset();
// remove the browser based timezone offset
date.setMinutes(date.getMinutes() - tz);
// add the event timezone offset
date.setMinutes(date.getMinutes() - utcOffsetInMinutes);

// Get the browser locale
const locale = navigator.language || navigator.languages[0];

// Specify the formatting options
const options: Intl.DateTimeFormatOptions = {
weekday: "long",
year: "numeric",
month: "long",
day: "numeric",
hour: "numeric",
minute: "numeric",
};

// Create an Intl.DateTimeFormat object
const formatter = new Intl.DateTimeFormat(locale, options);
// Format the date
const formattedDate = formatter.format(date);
return formattedDate;
};
67 changes: 65 additions & 2 deletions src/playground/src/components/event/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,19 @@ import {
} from "@aaronpowell/react-static-web-apps-auth";
import {
Button,
Dialog,
DialogActions,
DialogContent,
DialogSurface,
DialogTitle,
DialogTrigger,
Link,
makeStyles,
shorthands,
tokens,
} from "@fluentui/react-components";
import {} from "@fluentui/react-icons";
import { DeleteFilled, SignOutFilled } from "@fluentui/react-icons";
import { Form } from "react-router-dom";

const useStyles = makeStyles({
container: {
Expand All @@ -35,6 +43,10 @@ const useStyles = makeStyles({
logo: {
height: "24px",
},
warningButton: {
color: tokens.colorStatusDangerForeground1,
backgroundColor: tokens.colorStatusDangerBackground1,
},
});

export const Header = () => {
Expand Down Expand Up @@ -62,10 +74,17 @@ export const Header = () => {
{loaded && clientPrincipal && (
<>
Welcome {clientPrincipal.userDetails}&nbsp;
<DeleteAccount styles={styles} />
&nbsp;
<Logout
postLogoutRedirect={window.location.href}
customRenderer={({ href }) => (
<Button href={href} as="a" appearance="primary">
<Button
href={href}
as="a"
appearance="primary"
icon={<SignOutFilled />}
>
Logout
</Button>
)}
Expand All @@ -91,3 +110,47 @@ export const Header = () => {
</div>
);
};

const DeleteAccount = ({
styles,
}: {
styles: ReturnType<typeof useStyles>;
}) => {
return (
<Dialog>
<DialogTrigger>
<Button as="a" icon={<DeleteFilled />} className={styles.warningButton}>
Delete Account
</Button>
</DialogTrigger>
<DialogSurface>
<DialogTitle>Delete Account</DialogTitle>
<DialogContent>
Do you want to delete your account? This will render your API Key
unusable and log you out.
</DialogContent>
<DialogActions>
<DialogTrigger disableButtonEnhancement>
<Button appearance="primary">Cancel</Button>
</DialogTrigger>
<Logout
postLogoutRedirect={window.location.href}
customRenderer={({ href }) => (
<Form method="delete">
<input type="hidden" value={href} name="redirectUrl" />
<Button
icon={<DeleteFilled />}
type="submit"
as="button"
className={styles.warningButton}
>
Delete Account
</Button>
</Form>
)}
/>
</DialogActions>
</DialogSurface>
</Dialog>
);
};
6 changes: 4 additions & 2 deletions src/playground/src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ import { RouterProvider, createBrowserRouter } from "react-router-dom";
import "./index.css";
import {
Registration,
loader as registrationLoader,
registrationLoader,
Layout as EventLayout,
action as registrationAction,
registrationAction,
layoutAction,
} from "./pages/event";
import { Chat } from "./pages/playground/Chat";
import { Image } from "./pages/playground/Image";
Expand All @@ -32,6 +33,7 @@ const router = createBrowserRouter([
{
path: "/event",
element: <EventLayout />,
action: layoutAction,
children: [
{
path: ":id",
Expand Down
32 changes: 26 additions & 6 deletions src/playground/src/pages/event/Registration.action.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,43 @@
import { ActionFunction } from "react-router-dom";
import { API_VERSION } from "../../constants";

export const action: ActionFunction = async ({ params }) => {
export const action: ActionFunction = async ({ params, request }) => {
const id = params.id;

if (!id) {
throw new Response("Invalid event id", { status: 404 });
}

switch (request.method) {
case "POST":
await registerUser(id);
break;
case "DELETE":
await deregisterUser(id);
break;
case "PATCH":
await activateUser(id);
break;
default:
throw new Response("Invalid request method", { status: 405 });
}

return null;
};

const registerUser = (id: string) => executeRequest(id, "POST");
const deregisterUser = (id: string) => executeRequest(id, "DELETE");
const activateUser = (id: string) => executeRequest(id, "PATCH");

async function executeRequest(id: string, method: "POST" | "DELETE" | "PATCH") {
const response = await fetch(
`/api/${API_VERSION}/attendee/event/${id}/register`,
{
method: "POST",
method,
}
);

if (!response.ok) {
throw new Response("Failed to register", { status: 500 });
throw new Response(`Failed to ${method}`, { status: 500 });
}

return null;
};
}
Loading
Loading