Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 21 additions & 12 deletions frontend/src/modules/CreateCollectionModal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,22 @@ import { showNotification } from '@mantine/notifications';
import { createStudySBH, createStudyFJ } from '../API';
import { useState } from 'react';

function CreateCollectionModal({ opened, onClose, studyName, studyDescription, goBack }) {
function CreateCollectionModal({
opened,
onClose,
studyName,
studyDescription,
goBack,
createFlapjackStudy = false,
modalData = {},
}) {
const [instanceData, setInstanceData] = useLocalStorage({ key: "SynbioHub", defaultValue: [] });
const [instanceDataFJ, setInstanceDataFJ] = useLocalStorage({ key: "Flapjack", defaultValue: [] });
const selected = useSelector(state => state.primaryRepository.sbhPrimary);
const selectedFJ = useSelector(state => state.primaryRepository.fjPrimary);
const [overwrite, setOverwrite] = useState(false);
const studyId = makeIdentifier(studyName || "");
const shouldCreateFlapjackStudy = Boolean(modalData?.createFlapjackStudy ?? createFlapjackStudy);

function makeIdentifier(text) {
let id = text.trim()
Expand Down Expand Up @@ -60,12 +69,12 @@ function CreateCollectionModal({ opened, onClose, studyName, studyDescription, g
const registryURL = instance?.registryURL || url;
const registryPrefix = instance?.registryPrefix || url;

const urlFJ = selectedFJ && selectedFJ.trim() !== "" ? selectedFJ : null;
const instanceFJ = instanceDataFJ.find((inst) => inst.registryURL === urlFJ);
const authFJ = instanceFJ ? instanceFJ.authtoken : null;
const registryAPIFJ = instanceFJ?.registryAPI || urlFJ;
const registryURLFJ = instanceFJ?.registryURL || urlFJ;
const registryPrefixFJ = instanceFJ?.registryPrefix || urlFJ;
const urlFJ = shouldCreateFlapjackStudy && selectedFJ && selectedFJ.trim() !== "" ? selectedFJ : null;
const instanceFJ = shouldCreateFlapjackStudy ? instanceDataFJ.find((inst) => inst.registryURL === urlFJ) : null;
const authFJ = shouldCreateFlapjackStudy ? (instanceFJ ? instanceFJ.authtoken : null) : null;
const registryAPIFJ = shouldCreateFlapjackStudy ? (instanceFJ?.registryAPI || urlFJ) : null;
const registryURLFJ = shouldCreateFlapjackStudy ? (instanceFJ?.registryURL || urlFJ) : null;
const registryPrefixFJ = shouldCreateFlapjackStudy ? (instanceFJ?.registryPrefix || urlFJ) : null;

if (!url) {
showNotification({
Expand All @@ -81,7 +90,7 @@ function CreateCollectionModal({ opened, onClose, studyName, studyDescription, g
await createStudySBH(id, version, name, description, citations, auth, registryAPI, overwrite);

let FJid = null;
if (urlFJ) {
if (shouldCreateFlapjackStudy && urlFJ) {
FJid = await createStudyFJ(id, version, name, description, citations, authFJ, registryAPIFJ, overwrite);
}

Expand All @@ -104,10 +113,10 @@ function CreateCollectionModal({ opened, onClose, studyName, studyDescription, g
registryURL,
registryAPI,
registryPrefix,
FJid,
registryURLFJ,
registryAPIFJ,
registryPrefixFJ,
FJid: shouldCreateFlapjackStudy ? FJid : null,
registryURLFJ: shouldCreateFlapjackStudy ? registryURLFJ : null,
registryAPIFJ: shouldCreateFlapjackStudy ? registryAPIFJ : null,
registryPrefixFJ: shouldCreateFlapjackStudy ? registryPrefixFJ : null,
};

onClose(payload);
Expand Down
193 changes: 163 additions & 30 deletions frontend/src/modules/modular_login/FJLogin.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useEffect } from 'react';
import { useEffect, useState } from 'react';
import { useForm } from '@mantine/form';
import { TextInput, PasswordInput, Button, Modal } from '@mantine/core';
import { TextInput, PasswordInput, Button, Modal, Group } from '@mantine/core';
import { useLocalStorage } from '@mantine/hooks';
import axios from 'axios';
import { showNotification, cleanNotifications } from '@mantine/notifications';
Expand Down Expand Up @@ -37,8 +37,38 @@ const login = async (instance, username, password) => {
}
};

const register = async (instance, payload) => {
const registrationPayload = JSON.stringify({
username: payload.username,
password: payload.password,
password2: payload.password,
email: payload.email,
});

const response = await fetch(`${instance}/api/auth/register/`, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: registrationPayload,
});

const responseText = await response.text();
if (!response.ok) {
throw new Error(responseText || 'Registration failed');
}

try {
return JSON.parse(responseText) ?? { success: true };
} catch {
return responseText ? { success: true, message: responseText } : { success: true };
}
};

const FJInstanceLogin = ({ opened, onClose, goBack, setRepoSelection, selectedRepo, selectedFJRepo, onLoginSuccess }) => {
const [instanceData, setInstanceData] = useLocalStorage({ key: "Flapjack", defaultValue: [] });
const [registerOpened, setRegisterOpened] = useState(false);
const dispatch = useDispatch();
const selected = useSelector(state => state.primaryRepository.fjPrimary);
const setSelected = (value) => dispatch(setFJPrimary(typeof value === 'function' ? value(selected) : value));
Expand All @@ -63,7 +93,21 @@ const FJInstanceLogin = ({ opened, onClose, goBack, setRepoSelection, selectedRe
},
});

// Reset form when modal is opened
const registerForm = useForm({
initialValues: {
username: '',
email: '',
password: '',
confirmPassword: '',
},
validate: {
username: (value) => (value ? null : 'Username is required'),
email: (value) => (value ? null : 'Email is required'),
password: (value) => (value ? null : 'Password is required'),
confirmPassword: (value, values) => (value === values.password ? null : 'Passwords do not match'),
},
});

useEffect(() => {
if (opened) {
form.reset();
Expand Down Expand Up @@ -134,35 +178,124 @@ const FJInstanceLogin = ({ opened, onClose, goBack, setRepoSelection, selectedRe
}
};

const handleRegisterSubmit = async (values) => {
if (!repoToUse) {
showNotification({
title: 'Registration failed',
message: 'Please select a Flapjack repository before registering.',
color: 'red',
});
return;
}

try {
const existing = instanceData.find(item => item.registryURL === repoToUse) || {};
const registryAPI = existing.registryAPI || repoToUse;
await register(registryAPI, {
username: values.username,
email: values.email,
password: values.password,
});

cleanNotifications();
showNotification({
title: 'Registration successful',
message: 'Your Flapjack account has been created. You can now log in.',
color: 'green',
});
setRegisterOpened(false);
registerForm.reset();
form.setValues({ username: values.username, password: '' });
} catch (error) {
cleanNotifications();
const backendMessage = error.response?.data?.error || error.response?.data?.message || error.message || 'Unable to register your account.';
showNotification({
title: 'Registration failed',
message: typeof backendMessage === 'string' ? backendMessage : 'Unable to register your account.',
color: 'red',
});
}
};

return (
<Modal
opened={opened}
onClose={onClose}
title="Login to Flapjack"
>
<form
onSubmit={form.onSubmit((values) => {handleSubmit(values)})}
<>
<Modal
opened={opened}
onClose={onClose}
title="Login to Flapjack"
>
<form
onSubmit={form.onSubmit((values) => {handleSubmit(values)})}
>
<TextInput
label={"Username"}
placeholder={`Enter your username here`}
mt="md"
{...form.getInputProps('username')}
/>
<PasswordInput
label="Password"
placeholder="Enter your password"
mt="md"
{...form.getInputProps('password')}
/>
<Group position="apart" mt="xl">
<Button variant="default" onClick={() => {if(instanceData.length == 0) {setRepoSelection("")} else goBack(false)}}>
Back
</Button>
<Group spacing="sm">
<Button variant="default" onClick={() => setRegisterOpened(true)}>
Register
</Button>
<Button type="submit">
Login
</Button>
</Group>
</Group>
</form>
</Modal>

<Modal
opened={registerOpened}
onClose={() => setRegisterOpened(false)}
title="Register to Flapjack"
>
<TextInput
label={"Username"}
placeholder={`Enter your username here`}
mt="md"
{...form.getInputProps('username')}
/>
<PasswordInput
label="Password"
placeholder="Enter your password"
mt="md"
{...form.getInputProps('password')}
/>
<Button type="submit" mt="md">
Login
</Button>
<Button variant="outline" mt="md" ml="sm" onClick={() => {if(instanceData.length == 0) {setRepoSelection("")} else goBack(false)}}>
Back
</Button>
</form>
</Modal>
<form onSubmit={registerForm.onSubmit((values) => { handleRegisterSubmit(values); })}>
<TextInput
label="Username"
placeholder="Choose a username"
mt="md"
{...registerForm.getInputProps('username')}
/>
<TextInput
label="Email"
placeholder="Enter your email"
mt="md"
{...registerForm.getInputProps('email')}
/>
<PasswordInput
label="Password"
placeholder="Create a password"
mt="md"
{...registerForm.getInputProps('password')}
/>
<PasswordInput
label="Confirm password"
placeholder="Repeat your password"
mt="md"
{...registerForm.getInputProps('confirmPassword')}
/>
<Group position="right" mt="xl">
<Button variant="default" onClick={() => setRegisterOpened(false)}>
Cancel
</Button>
<Button type="submit">
Create account
</Button>
</Group>
</form>
</Modal>
</>
);
};

Expand Down
Loading
Loading