Compare commits
1 Commits
0ec6e7525b
...
c9bcbdbb5d
Author | SHA1 | Date | |
---|---|---|---|
|
c9bcbdbb5d |
@ -1,28 +1,30 @@
|
|||||||
/* Copyright (C) 2024 Manuel Bustillo*/
|
/* Copyright (C) 2024 Manuel Bustillo*/
|
||||||
|
|
||||||
'use client';
|
'use client';
|
||||||
|
import Link from 'next/link';
|
||||||
|
import styles from '@/app/ui/home.module.css';
|
||||||
import LoginForm from '@/app/ui/components/login-form';
|
import LoginForm from '@/app/ui/components/login-form';
|
||||||
import RegistrationForm from '@/app/ui/components/registration-form';
|
|
||||||
import { useParams } from 'next/navigation'
|
|
||||||
|
|
||||||
export default async function Page() {
|
|
||||||
const params = useParams<{ slug: string }>()
|
|
||||||
localStorage.setItem('slug', await params.slug);
|
|
||||||
|
|
||||||
|
|
||||||
|
export default async function Page({ params }: { params: Promise<{ slug: string }> }) {
|
||||||
|
|
||||||
|
localStorage.setItem('slug', (await params).slug)
|
||||||
return (
|
return (
|
||||||
<main className="flex min-h-screen flex-col p-6">
|
<main className="flex min-h-screen flex-col p-6">
|
||||||
<div className="flex flex-row">
|
<div className="flex flex-row">
|
||||||
<div className="w-1/2">
|
<div className="w-1/2">
|
||||||
|
|
||||||
Already have an account? Sign in
|
Already have an account? Sign in
|
||||||
|
|
||||||
<LoginForm />
|
<LoginForm />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="w-1/2">
|
<div className="w-1/2">
|
||||||
Don't have an account? Register now!
|
Don't have an account? Register now!
|
||||||
<RegistrationForm />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
/* Copyright (C) 2024 Manuel Bustillo*/
|
/* Copyright (C) 2024 Manuel Bustillo*/
|
||||||
|
|
||||||
import { asArray, getCsrfToken, getSlug } from '@/app/lib/utils';
|
import { getCsrfToken, getSlug } from '@/app/lib/utils';
|
||||||
import { Captcha, StructuredErrors, User } from '@/app/lib/definitions';
|
import { User } from '@/app/lib/definitions';
|
||||||
|
|
||||||
export function login({ email, password, onLogin }: { email: string, password: string, onLogin: (user: User) => void }) {
|
export function login({ email, password, onLogin }: { email: string, password: string, onLogin: (user: User) => void }) {
|
||||||
|
console.log(email, password);
|
||||||
return fetch(`/api/${getSlug()}/users/sign_in`, {
|
return fetch(`/api/${getSlug()}/users/sign_in`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: JSON.stringify({ user: { email, password } }),
|
body: JSON.stringify({ user: { email, password } }),
|
||||||
@ -33,45 +34,3 @@ export function logout({ onLogout }: { onLogout: () => void }) {
|
|||||||
}).then(onLogout)
|
}).then(onLogout)
|
||||||
.catch((error) => console.error(error));
|
.catch((error) => console.error(error));
|
||||||
}
|
}
|
||||||
|
|
||||||
function flattenErrors(errors: StructuredErrors): string[] {
|
|
||||||
if(errors instanceof Array) {
|
|
||||||
return errors;
|
|
||||||
}
|
|
||||||
return Object.keys(errors).map((key) => {
|
|
||||||
return `${key}: ${asArray(errors[key]).join(', ')}`;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function register({slug, email, password, passwordConfirmation, captcha, onRegister, onError}: {
|
|
||||||
slug: string,
|
|
||||||
email: string,
|
|
||||||
password: string,
|
|
||||||
passwordConfirmation: string,
|
|
||||||
captcha: Captcha,
|
|
||||||
onRegister: () => void,
|
|
||||||
onError: (errors: string[]) => void
|
|
||||||
}){
|
|
||||||
fetch(`/api/${slug}/users`, {
|
|
||||||
method: 'POST',
|
|
||||||
body: JSON.stringify(
|
|
||||||
{
|
|
||||||
user: { email, password, password_confirmation: passwordConfirmation },
|
|
||||||
captcha: { id: captcha.id, answer: captcha.answer }
|
|
||||||
}
|
|
||||||
),
|
|
||||||
headers: {
|
|
||||||
'Accept': 'application/json',
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
'X-CSRF-TOKEN': getCsrfToken(),
|
|
||||||
}
|
|
||||||
}).then((response) => {
|
|
||||||
if(response.ok) {
|
|
||||||
response.json().then(onRegister);
|
|
||||||
} else {
|
|
||||||
response.json().then((data: any) => {
|
|
||||||
onError(data.errors && flattenErrors(data.errors) || [data.error]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
@ -1,19 +0,0 @@
|
|||||||
/* Copyright (C) 2024 Manuel Bustillo*/
|
|
||||||
|
|
||||||
import { data } from "autoprefixer";
|
|
||||||
import { getCsrfToken } from "../lib/utils";
|
|
||||||
|
|
||||||
export function getCaptchaChallenge({onRetrieve}: {onRetrieve: (id: string, url: string) => void}){
|
|
||||||
return fetch('/api/captcha', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Accept': 'application/json',
|
|
||||||
'X-CSRF-TOKEN': getCsrfToken(),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.then((response) => response.json())
|
|
||||||
.then((data: any) => {
|
|
||||||
onRetrieve(data.id, data.media_url)
|
|
||||||
})
|
|
||||||
.catch((error) => console.error(error));
|
|
||||||
}
|
|
@ -64,13 +64,4 @@ export type guestsTable = {
|
|||||||
export type User = {
|
export type User = {
|
||||||
id: string;
|
id: string;
|
||||||
email: string;
|
email: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Captcha = {
|
|
||||||
id: string;
|
|
||||||
answer: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type StructuredErrors = {
|
|
||||||
[key: string]: string[]|string;
|
|
||||||
};
|
|
@ -13,8 +13,3 @@ export const getSlug = () => localStorage.getItem('slug') || 'default';
|
|||||||
export const capitalize = (val:string) => {
|
export const capitalize = (val:string) => {
|
||||||
return String(val).charAt(0).toUpperCase() + String(val).slice(1);
|
return String(val).charAt(0).toUpperCase() + String(val).slice(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// From https://stackoverflow.com/a/62118163/3607039
|
|
||||||
export function asArray<T>(value: T | T[]): T[] {
|
|
||||||
return ([] as T[]).concat(value)
|
|
||||||
}
|
|
@ -1,98 +0,0 @@
|
|||||||
/* Copyright (C) 2024 Manuel Bustillo*/
|
|
||||||
|
|
||||||
'use client';
|
|
||||||
|
|
||||||
import { FloatLabel } from 'primereact/floatlabel';
|
|
||||||
import { InputText } from 'primereact/inputtext';
|
|
||||||
import { useState, useEffect } from 'react';
|
|
||||||
import { classNames } from './button';
|
|
||||||
import { getSlug } from '@/app/lib/utils';
|
|
||||||
import { register } from '@/app/api/authentication';
|
|
||||||
import { getCaptchaChallenge } from '@/app/api/captcha';
|
|
||||||
|
|
||||||
export default function RegistrationForm() {
|
|
||||||
const [submitted, setSubmitted] = useState<boolean>(false);
|
|
||||||
const [errors, setErrors] = useState<string[]>([]);
|
|
||||||
|
|
||||||
const [email, setEmail] = useState<string>("");
|
|
||||||
const [password, setPassword] = useState<string>("");
|
|
||||||
const [passwordConfirmation, setPasswordConfirmation] = useState<string>("");
|
|
||||||
const [slug, setSlug] = useState<string>(getSlug());
|
|
||||||
|
|
||||||
const [captchaId, setCaptchaId] = useState<string>("");
|
|
||||||
const [captchaUrl, setCaptchaUrl] = useState<string>("");
|
|
||||||
const [captchaAnswer, setCaptchaAnswer] = useState<string>("");
|
|
||||||
|
|
||||||
const refreshCaptcha = () => {
|
|
||||||
getCaptchaChallenge({
|
|
||||||
onRetrieve: (id, url) => {
|
|
||||||
console.log(id, url);
|
|
||||||
setCaptchaId(id);
|
|
||||||
setCaptchaUrl(url);
|
|
||||||
setCaptchaAnswer("");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(refreshCaptcha, [])
|
|
||||||
|
|
||||||
return (
|
|
||||||
submitted ? (
|
|
||||||
<div className="card flex justify-evenly py-5 flex-col">
|
|
||||||
<div className="text-green-500">Registration successful. Check your email for a confirmation link.</div>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
|
|
||||||
<div className="card flex justify-evenly py-5 flex-col">
|
|
||||||
<FloatLabel className="my-4">
|
|
||||||
<InputText id="email" type="email" className='rounded-sm' onChange={(e) => setEmail(e.target.value)} />
|
|
||||||
<label htmlFor="email">Email</label>
|
|
||||||
</FloatLabel>
|
|
||||||
<FloatLabel className="my-4">
|
|
||||||
<InputText id="password" type="password" className='rounded-sm' onChange={(e) => setPassword(e.target.value)} />
|
|
||||||
<label htmlFor="password">Password</label>
|
|
||||||
</FloatLabel>
|
|
||||||
<FloatLabel className="my-4">
|
|
||||||
<InputText id="passwordConfirmation" type="password" className='rounded-sm' onChange={(e) => setPasswordConfirmation(e.target.value)} />
|
|
||||||
<label htmlFor="passwordConfirmation">Confirm Password</label>
|
|
||||||
</FloatLabel>
|
|
||||||
<FloatLabel className="my-4">
|
|
||||||
<InputText id="slug" type="text" className='rounded-sm' onChange={(e) => setSlug(e.target.value)} />
|
|
||||||
<label htmlFor="slug">Slug</label>
|
|
||||||
</FloatLabel>
|
|
||||||
<img className="w-96" src={captchaUrl} alt="captcha" />
|
|
||||||
<FloatLabel className="my-4">
|
|
||||||
<InputText id="captcha" type="text" className='rounded-sm' value={captchaAnswer} onChange={(e) => setCaptchaAnswer(e.target.value)} />
|
|
||||||
<label htmlFor="captcha">Captcha</label>
|
|
||||||
</FloatLabel>
|
|
||||||
|
|
||||||
{errors.map((error, index) => (
|
|
||||||
<div key={index} className="text-red-500">{error}</div>
|
|
||||||
))}
|
|
||||||
|
|
||||||
|
|
||||||
<button
|
|
||||||
className={classNames('primary')}
|
|
||||||
disabled={!(email && password && passwordConfirmation && slug && captchaAnswer)}
|
|
||||||
onClick={() => register(
|
|
||||||
{
|
|
||||||
slug: slug,
|
|
||||||
email: email,
|
|
||||||
password: password,
|
|
||||||
passwordConfirmation: passwordConfirmation,
|
|
||||||
captcha: {
|
|
||||||
id: captchaId,
|
|
||||||
answer: captchaAnswer
|
|
||||||
},
|
|
||||||
onRegister: () => { setErrors([]); setSubmitted(true) },
|
|
||||||
onError: (errors) => { refreshCaptcha(); setErrors(errors) }
|
|
||||||
}
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
Register
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
|
|
||||||
);
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user