Skip to content

Commit 562ec4a

Browse files
committed
add login dialog
1 parent d1e5df4 commit 562ec4a

4 files changed

Lines changed: 92 additions & 31 deletions

File tree

apps/playground/src/components/Header/ActionsDropdown/ActionsDropdown.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { EllipsisVerticalIcon } from 'lucide-react';
66
import { useAppStore } from '@/store';
77

88
import { DeleteInstrumentDialog } from './DeleteInstrumentDialog';
9+
import { LoginDialog } from './LoginDialog';
910
import { RestoreDefaultsDialog } from './RestoreDefaultsDialog';
1011
import { UploadBundleDialog } from './UploadBundleDialog';
1112
import { UserSettingsDialog } from './UserSettingsDialog';
@@ -15,6 +16,7 @@ export const ActionsDropdown = () => {
1516
const [showDeleteInstrumentDialog, setShowDeleteInstrumentDialog] = useState(false);
1617
const [showRestoreDefaultsDialog, setShowRestoreDefaultsDialog] = useState(false);
1718
const [showUploadBundleDialog, setShowUploadBundleDialog] = useState(false);
19+
const [showLoginDialog, setShowLoginDialog] = useState(false);
1820

1921
const selectedInstrument = useAppStore((store) => store.selectedInstrument);
2022

@@ -37,6 +39,11 @@ export const ActionsDropdown = () => {
3739
<span>GitHub</span>
3840
</a>
3941
</DropdownMenu.Item>
42+
<DropdownMenu.Item asChild onSelect={() => setShowLoginDialog(true)}>
43+
<button className="w-full cursor-pointer disabled:cursor-not-allowed disabled:opacity-50" type="button">
44+
Login
45+
</button>
46+
</DropdownMenu.Item>
4047
<DropdownMenu.Item asChild onSelect={() => setShowUploadBundleDialog(true)}>
4148
<button className="w-full cursor-pointer disabled:cursor-not-allowed disabled:opacity-50" type="button">
4249
Upload Bundle
@@ -71,6 +78,7 @@ export const ActionsDropdown = () => {
7178
<DeleteInstrumentDialog isOpen={showDeleteInstrumentDialog} setIsOpen={setShowDeleteInstrumentDialog} />
7279
<RestoreDefaultsDialog isOpen={showRestoreDefaultsDialog} setIsOpen={setShowRestoreDefaultsDialog} />
7380
<UploadBundleDialog isOpen={showUploadBundleDialog} setIsOpen={setShowUploadBundleDialog} />
81+
<LoginDialog isOpen={showLoginDialog} setIsOpen={setShowLoginDialog} />
7482
</React.Fragment>
7583
);
7684
};
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/* eslint-disable perfectionist/sort-objects */
2+
3+
import { Dialog, Form } from '@douglasneuroinformatics/libui/components';
4+
import { useNotificationsStore } from '@douglasneuroinformatics/libui/hooks';
5+
import { z } from 'zod/v4';
6+
7+
import { useAppStore } from '@/store';
8+
9+
type $LoginData = z.infer<typeof $LoginData>;
10+
const $LoginData = z.object({
11+
apiBaseUrl: z.url(),
12+
username: z.string().min(1),
13+
password: z.string().min(1)
14+
});
15+
16+
export type LoginDialogProps = {
17+
isOpen: boolean;
18+
setIsOpen: (value: boolean) => void;
19+
};
20+
21+
export const LoginDialog = ({ isOpen, setIsOpen }: LoginDialogProps) => {
22+
const apiBaseUrl = useAppStore((store) => store.settings.apiBaseUrl);
23+
const updateSettings = useAppStore((store) => store.updateSettings);
24+
25+
const addNotification = useNotificationsStore((store) => store.addNotification);
26+
27+
const handleSubmit = ({ apiBaseUrl }: $LoginData) => {
28+
updateSettings({ apiBaseUrl });
29+
addNotification({ type: 'success' });
30+
};
31+
32+
return (
33+
<Dialog open={isOpen} onOpenChange={setIsOpen}>
34+
<Dialog.Content onOpenAutoFocus={(event) => event.preventDefault()}>
35+
<Dialog.Header>
36+
<Dialog.Title>Login</Dialog.Title>
37+
<Dialog.Description>
38+
Login to your Open Data Capture instance. A special access token is used that grants permissions to create
39+
instruments only. You must have permission to create instruments to use this functionality.
40+
</Dialog.Description>
41+
</Dialog.Header>
42+
<Dialog.Body className="grid gap-4">
43+
<Form
44+
content={[
45+
{
46+
title: 'Instance Settings',
47+
fields: {
48+
apiBaseUrl: {
49+
description: 'The base path for your Open Data Capture REST API.',
50+
kind: 'string',
51+
placeholder: 'e.g., https://demo.opendatacapture.org/api',
52+
label: 'API Base URL',
53+
variant: 'input'
54+
}
55+
}
56+
},
57+
{
58+
title: 'Login Credentials',
59+
fields: {
60+
username: {
61+
kind: 'string',
62+
label: 'Username',
63+
variant: 'input'
64+
},
65+
password: {
66+
kind: 'string',
67+
label: 'Password',
68+
variant: 'password'
69+
}
70+
}
71+
}
72+
]}
73+
initialValues={{ apiBaseUrl }}
74+
validationSchema={$LoginData}
75+
onSubmit={(data) => {
76+
void handleSubmit(data);
77+
setIsOpen(false);
78+
}}
79+
/>
80+
</Dialog.Body>
81+
</Dialog.Content>
82+
</Dialog>
83+
);
84+
};

apps/playground/src/components/Header/LoginForm/LoginForm.tsx

Lines changed: 0 additions & 30 deletions
This file was deleted.

apps/playground/src/components/Header/LoginForm/index.ts

Lines changed: 0 additions & 1 deletion
This file was deleted.

0 commit comments

Comments
 (0)