Remove unused components and models for improved maintainability
- Deleted redundant components (`AddActionButton`, `AlertBox`, `AlertStack`, `BackButton`, `CancelButton`, and `CollapsableArea`) and related files. - Removed unused models (`Book`, `BookSerie`, `BookTables`, `Character`, and `Chapter`) to reduce codebase clutter. - Updated project structure and references to reflect these removals.
This commit is contained in:
@@ -1,196 +1,198 @@
|
||||
'use client';
|
||||
|
||||
import {ChangeEvent, Dispatch, SetStateAction, useContext, useState} from 'react';
|
||||
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
|
||||
import {faApple, faGooglePlay} from '@fortawesome/free-brands-svg-icons';
|
||||
import {faCheck, faKey, faMobileAlt, faQrcode} from '@fortawesome/free-solid-svg-icons';
|
||||
import System from "@/lib/models/System";
|
||||
import {AlertContext, AlertContextProps} from "@/context/AlertContext";
|
||||
import {FormResponse} from "@/shared/interface";
|
||||
import {SessionContext} from "@/context/SessionContext";
|
||||
import TextInput from "@/components/form/TextInput";
|
||||
|
||||
export default function TwoFactorSetup({setShowSetup}: { setShowSetup: Dispatch<SetStateAction<boolean>> }) {
|
||||
const {session} = useContext(SessionContext);
|
||||
const alert: AlertContextProps = useContext(AlertContext);
|
||||
|
||||
const [step, setStep] = useState<number>(1);
|
||||
const [token, setToken] = useState<string>('')
|
||||
const [qrCode, setQrCode] = useState<string | null>(null);
|
||||
const [loadingQRCode, setLoadingQRCode] = useState(false);
|
||||
|
||||
async function getQRCode() {
|
||||
try {
|
||||
const response: { qrCode: string } = await System.authPostToServer('twofactor/setup', {
|
||||
email: session?.user?.email,
|
||||
}, session?.accessToken ?? '');
|
||||
setQrCode(response.qrCode);
|
||||
} catch (e: any) {
|
||||
alert.errorMessage(e.message);
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
async function handleNextStep() {
|
||||
if (step === 3) {
|
||||
await validateToken();
|
||||
} else if (step === 1) {
|
||||
if (qrCode === null) {
|
||||
getQRCode();
|
||||
}
|
||||
setStep((prev: number) => Math.min(prev + 1, 3));
|
||||
} else {
|
||||
setStep((prev: number) => Math.min(prev + 1, 3));
|
||||
}
|
||||
}
|
||||
|
||||
async function validateToken() {
|
||||
try {
|
||||
const response: FormResponse = await System.authPostToServer('twofactor/activate', {
|
||||
email: session?.user?.email, token: token
|
||||
}, session?.accessToken ?? '');
|
||||
if (response.valid) {
|
||||
alert.successMessage(response.message ?? '');
|
||||
setShowSetup(false);
|
||||
}
|
||||
} catch (e: any) {
|
||||
alert.errorMessage(e.message);
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
function handlePrevStep() {
|
||||
setStep((prev) => Math.max(prev - 1, 1));
|
||||
}
|
||||
|
||||
function getProgressClass(currentStep: number) {
|
||||
return `flex-grow h-2.5 rounded-full transition-all duration-300 ${
|
||||
step >= currentStep ? 'bg-primary shadow-sm' : 'bg-secondary/50'
|
||||
}`;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className="rounded-2xl shadow-2xl bg-tertiary/90 backdrop-blur-sm m-auto p-8 w-full border border-secondary/50">
|
||||
<h2 className="text-2xl font-['ADLaM_Display'] text-text-primary text-center mb-6">
|
||||
Setup Two-Factor Authentication
|
||||
</h2>
|
||||
|
||||
{/* Step Indicator */}
|
||||
<div className="mb-8">
|
||||
<div className="flex items-center">
|
||||
<div className={getProgressClass(1)}></div>
|
||||
<div className="w-4"></div>
|
||||
<div className={getProgressClass(2)}></div>
|
||||
<div className="w-4"></div>
|
||||
<div className={getProgressClass(3)}></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Step Content */}
|
||||
<div className="mb-6">
|
||||
{step === 1 && (
|
||||
<div className="text-muted">
|
||||
<p className="mb-4 text-text-primary">
|
||||
Follow these steps to enable two-factor authentication for your account:
|
||||
</p>
|
||||
<ol className="list-decimal list-inside space-y-3">
|
||||
<li className="flex items-start">
|
||||
<FontAwesomeIcon icon={faMobileAlt} className="text-primary mr-3 mt-1"/>
|
||||
Download a two-factor authentication app like Google Authenticator or Authy.
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<FontAwesomeIcon icon={faCheck} className="text-primary mr-3 mt-1"/>
|
||||
Open the app and select the option to scan a QR code.
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<FontAwesomeIcon icon={faQrcode} className="text-primary mr-3 mt-1"/>
|
||||
Proceed to the next step to scan the QR code provided.
|
||||
</li>
|
||||
</ol>
|
||||
<div className="mt-6 space-y-4">
|
||||
<a
|
||||
href="https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="flex items-center justify-center px-4 py-3 bg-primary text-text-primary rounded-xl hover:bg-primary-dark transition-all duration-200 hover:scale-105 shadow-md hover:shadow-lg font-medium"
|
||||
>
|
||||
<FontAwesomeIcon icon={faGooglePlay} className="mr-2"/>
|
||||
Download on Google Play
|
||||
</a>
|
||||
<a
|
||||
href="https://apps.apple.com/app/google-authenticator/id388497605"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="flex items-center justify-center px-4 py-3 bg-primary text-text-primary rounded-xl hover:bg-primary-dark transition-all duration-200 hover:scale-105 shadow-md hover:shadow-lg font-medium"
|
||||
>
|
||||
<FontAwesomeIcon icon={faApple} className="mr-2"/>
|
||||
Download on App Store
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{step === 2 && (
|
||||
<div className="text-muted text-center">
|
||||
<p className="mb-4 text-text-primary">
|
||||
Scan the QR code below with your authentication app to link your account.
|
||||
</p>
|
||||
<div className="flex justify-center">
|
||||
<div className="bg-secondary/20 p-6 rounded-xl shadow-lg border border-secondary/50">
|
||||
{loadingQRCode ? (
|
||||
<div className="text-muted">Loading QR Code...</div>
|
||||
) : qrCode ? (
|
||||
<img src={qrCode} alt="QR Code" className="w-48 h-48 mx-auto"/>
|
||||
) : (
|
||||
<div className="text-muted">Failed to load QR Code.</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<p className="mt-4 text-sm text-muted">
|
||||
Having trouble? Make sure your app supports QR code scanning.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
{step === 3 && (
|
||||
<div className="text-text-secondary">
|
||||
<p className="mb-4">
|
||||
Enter the 6-digit code generated by your authentication app to verify the setup.
|
||||
</p>
|
||||
<div className="relative">
|
||||
<TextInput
|
||||
value={token}
|
||||
setValue={(e: ChangeEvent<HTMLInputElement>) => setToken(e.target.value)}
|
||||
placeholder="Enter 6-digit code"
|
||||
/>
|
||||
<FontAwesomeIcon
|
||||
icon={faKey}
|
||||
className="absolute right-3 top-3 text-muted pointer-events-none"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Navigation Buttons */}
|
||||
<div className="flex justify-between">
|
||||
<button
|
||||
onClick={handlePrevStep}
|
||||
disabled={step === 1}
|
||||
className={`px-6 py-2.5 rounded-xl transition-all duration-200 font-medium ${
|
||||
step === 1 ? 'bg-secondary/30 text-muted cursor-not-allowed' : 'bg-secondary/50 hover:bg-secondary text-text-primary hover:scale-105 shadow-sm border border-secondary/50'
|
||||
}`}
|
||||
>
|
||||
Back
|
||||
</button>
|
||||
<button
|
||||
onClick={handleNextStep}
|
||||
className={`px-6 py-2.5 rounded-xl transition-all duration-200 font-medium ${
|
||||
step === 3 ? 'bg-success hover:bg-success/90 text-text-primary shadow-md hover:shadow-lg hover:scale-105' : 'bg-primary hover:bg-primary-dark text-text-primary shadow-md hover:shadow-lg hover:scale-105'
|
||||
}`}
|
||||
>
|
||||
{step === 3 ? 'Finish' : 'Next'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
'use client';
|
||||
|
||||
import {ChangeEvent, Dispatch, SetStateAction, useContext, useState} from 'react';
|
||||
import {Check, KeyRound, Smartphone, QrCode} from 'lucide-react';
|
||||
import {apiPost} from "@/lib/api/client";
|
||||
import {AlertContext, AlertContextProps} from "@/context/AlertContext";
|
||||
import {FormResponse} from "@/shared/interface";
|
||||
import {SessionContext} from "@/context/SessionContext";
|
||||
import TextInput from "@/components/form/TextInput";
|
||||
import InputField from "@/components/form/InputField";
|
||||
import Button from "@/components/ui/Button";
|
||||
|
||||
const AppleIcon = () => (
|
||||
<svg className="w-5 h-5" viewBox="0 0 384 512" fill="currentColor">
|
||||
<path d="M318.7 268.7c-.2-36.7 16.4-64.4 50-84.8-18.8-26.9-47.2-41.7-84.7-44.6-35.5-2.8-74.3 20.7-88.5 20.7-15 0-49.4-19.7-76.4-19.7C63.3 141.2 4 184.8 4 273.5q0 39.3 14.4 81.2c12.8 36.7 59 126.7 107.2 125.2 25.2-.6 43-17.9 75.8-17.9 31.8 0 48.3 17.9 76.4 17.9 48.6-.7 90.4-82.5 102.6-119.3-65.2-30.7-61.7-90-61.7-91.9zm-56.6-164.2c27.3-32.4 24.8-61.9 24-72.5-24.1 1.4-52 16.4-67.9 34.9-17.5 19.8-27.8 44.3-25.6 71.9 26.1 2 49.9-11.4 69.5-34.3z"/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
const GooglePlayIcon = () => (
|
||||
<svg className="w-5 h-5" viewBox="0 0 512 512" fill="currentColor">
|
||||
<path d="M325.3 234.3L104.6 13l280.8 161.2-60.1 60.1zM47 0C34 6.8 25.3 19.2 25.3 35.3v441.3c0 16.1 8.7 28.5 21.7 35.3l256.6-256L47 0zm425.2 225.6l-58.9-34.1-65.7 64.5 65.7 64.5 60.1-34.1c18-14.3 18-46.5-1.2-60.8zM104.6 499l280.8-161.2-60.1-60.1L104.6 499z"/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default function TwoFactorSetup({setShowSetup}: { setShowSetup: Dispatch<SetStateAction<boolean>> }) {
|
||||
const {session} = useContext(SessionContext);
|
||||
const alert: AlertContextProps = useContext(AlertContext);
|
||||
|
||||
const [step, setStep] = useState<number>(1);
|
||||
const [token, setToken] = useState<string>('')
|
||||
const [qrCode, setQrCode] = useState<string | null>(null);
|
||||
const [loadingQRCode, setLoadingQRCode] = useState(false);
|
||||
|
||||
async function getQRCode() {
|
||||
try {
|
||||
const response: { qrCode: string } = await apiPost('twofactor/setup', {
|
||||
email: session?.user?.email,
|
||||
}, session?.accessToken ?? '');
|
||||
setQrCode(response.qrCode);
|
||||
} catch (e: unknown) {
|
||||
if (e instanceof Error) alert.errorMessage(e.message);
|
||||
}
|
||||
}
|
||||
|
||||
async function handleNextStep() {
|
||||
if (step === 3) {
|
||||
await validateToken();
|
||||
} else if (step === 1) {
|
||||
if (qrCode === null) {
|
||||
getQRCode();
|
||||
}
|
||||
setStep((prev: number) => Math.min(prev + 1, 3));
|
||||
} else {
|
||||
setStep((prev: number) => Math.min(prev + 1, 3));
|
||||
}
|
||||
}
|
||||
|
||||
async function validateToken() {
|
||||
try {
|
||||
const response: FormResponse = await apiPost('twofactor/activate', {
|
||||
email: session?.user?.email, token: token
|
||||
}, session?.accessToken ?? '');
|
||||
if (response.valid) {
|
||||
alert.successMessage(response.message ?? '');
|
||||
setShowSetup(false);
|
||||
}
|
||||
} catch (e: unknown) {
|
||||
if (e instanceof Error) alert.errorMessage(e.message);
|
||||
}
|
||||
}
|
||||
|
||||
function handlePrevStep() {
|
||||
setStep((prev) => Math.max(prev - 1, 1));
|
||||
}
|
||||
|
||||
function getProgressClass(currentStep: number) {
|
||||
return `flex-grow h-2.5 rounded-full transition-all duration-300 ${
|
||||
step >= currentStep ? 'bg-primary shadow-sm' : 'bg-secondary/50'
|
||||
}`;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="rounded-2xl shadow-2xl bg-tertiary/90 backdrop-blur-sm m-auto p-8 w-full border border-secondary/50">
|
||||
<h2 className="text-2xl font-['ADLaM_Display'] text-text-primary text-center mb-6">
|
||||
Setup Two-Factor Authentication
|
||||
</h2>
|
||||
|
||||
<div className="mb-8">
|
||||
<div className="flex items-center">
|
||||
<div className={getProgressClass(1)}></div>
|
||||
<div className="w-4"></div>
|
||||
<div className={getProgressClass(2)}></div>
|
||||
<div className="w-4"></div>
|
||||
<div className={getProgressClass(3)}></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mb-6">
|
||||
{step === 1 && (
|
||||
<div className="text-muted">
|
||||
<p className="mb-4 text-text-primary">
|
||||
Follow these steps to enable two-factor authentication for your account:
|
||||
</p>
|
||||
<ol className="list-decimal list-inside space-y-3">
|
||||
<li className="flex items-start">
|
||||
<Smartphone className="w-5 h-5 text-primary mr-3 mt-0.5 flex-shrink-0"/>
|
||||
Download a two-factor authentication app like Google Authenticator or Authy.
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<Check className="w-5 h-5 text-primary mr-3 mt-0.5 flex-shrink-0"/>
|
||||
Open the app and select the option to scan a QR code.
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<QrCode className="w-5 h-5 text-primary mr-3 mt-0.5 flex-shrink-0"/>
|
||||
Proceed to the next step to scan the QR code provided.
|
||||
</li>
|
||||
</ol>
|
||||
<div className="mt-6 space-y-4">
|
||||
<a
|
||||
href="https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="flex items-center justify-center gap-2 px-4 py-3 bg-primary text-text-primary rounded-xl hover:bg-primary-dark transition-all duration-200 hover:scale-105 shadow-md hover:shadow-lg font-medium"
|
||||
>
|
||||
<GooglePlayIcon/>
|
||||
Download on Google Play
|
||||
</a>
|
||||
<a
|
||||
href="https://apps.apple.com/app/google-authenticator/id388497605"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="flex items-center justify-center gap-2 px-4 py-3 bg-primary text-text-primary rounded-xl hover:bg-primary-dark transition-all duration-200 hover:scale-105 shadow-md hover:shadow-lg font-medium"
|
||||
>
|
||||
<AppleIcon/>
|
||||
Download on App Store
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{step === 2 && (
|
||||
<div className="text-muted text-center">
|
||||
<p className="mb-4 text-text-primary">
|
||||
Scan the QR code below with your authentication app to link your account.
|
||||
</p>
|
||||
<div className="flex justify-center">
|
||||
<div className="bg-secondary/20 p-6 rounded-xl shadow-lg border border-secondary/50">
|
||||
{loadingQRCode ? (
|
||||
<div className="text-muted">Loading QR Code...</div>
|
||||
) : qrCode ? (
|
||||
<img src={qrCode} alt="QR Code" className="w-48 h-48 mx-auto"/>
|
||||
) : (
|
||||
<div className="text-muted">Failed to load QR Code.</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<p className="mt-4 text-sm text-muted">
|
||||
Having trouble? Make sure your app supports QR code scanning.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
{step === 3 && (
|
||||
<div className="text-text-secondary">
|
||||
<p className="mb-4">
|
||||
Enter the 6-digit code generated by your authentication app to verify the setup.
|
||||
</p>
|
||||
<InputField
|
||||
icon={KeyRound}
|
||||
fieldName="Verification Code"
|
||||
input={
|
||||
<TextInput
|
||||
value={token}
|
||||
setValue={(e: ChangeEvent<HTMLInputElement>) => setToken(e.target.value)}
|
||||
placeholder="Enter 6-digit code"
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex justify-between">
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={handlePrevStep}
|
||||
disabled={step === 1}
|
||||
>
|
||||
Back
|
||||
</Button>
|
||||
<Button
|
||||
variant={step === 3 ? 'success' : 'primary'}
|
||||
onClick={handleNextStep}
|
||||
>
|
||||
{step === 3 ? 'Finish' : 'Next'}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user