Skip to content
Draft
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