reset password request authentication and route to the login page

This commit is contained in:
surajb 2024-07-23 13:42:51 +05:30
parent 89d5b17dc1
commit fc73ddefca
12 changed files with 23214 additions and 1275 deletions

15828
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -16,6 +16,7 @@
"proxy": "http://localhost:5000",
"dependencies": {
"@ant-design/icons": "^5.3.7",
"@craco/craco": "^7.1.0",
"@fortawesome/fontawesome-svg-core": "^6.6.0",
"@fortawesome/free-solid-svg-icons": "^6.6.0",
"@fortawesome/react-fontawesome": "^0.2.2",
@ -30,14 +31,14 @@
"@types/react": "^18.2.66",
"@types/react-dom": "^18.2.22",
"@vitejs/plugin-react": "^4.2.1",
"autoprefixer": "^10.4.19",
"autoprefixer": "^9.8.8",
"eslint": "^8.57.0",
"eslint-plugin-react": "^7.34.1",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.6",
"json-server": "^1.0.0-beta.1",
"postcss": "^8.4.39",
"tailwindcss": "^3.4.6",
"postcss": "^7.0.39",
"tailwindcss": "npm:@tailwindcss/postcss7-compat@^2.2.17",
"vite": "^5.2.0"
}
}

View File

@ -2,6 +2,7 @@ import React, { useContext } from 'react';
import { BrowserRouter as Router, Route, Routes, Navigate } from 'react-router-dom';
import { Layout } from 'antd';
import LoginPage from './components/Auth/LoginPage';
import ResetPasswordRequest from './components/Auth/ResetPasswordRequest';
import AdminDashboard from './components/admin/AdminDashboard';
import EmployeeDashboard from './components/employee/EmployeeDashboard';
import CustomerList from './components/customer/CustomerList';
@ -25,6 +26,7 @@ const App = () => {
<Router>
<Routes>
<Route exact path="/" element={<LoginPage />} />
<Route path="/reset-password" element={<ResetPasswordRequest />} />
<Route path="/employee/home" element={<EmployeeDashboard />} /> {/* Add HomePage route */}
<Route path="/admin" element={<PrivateRoute><AdminDashboard /></PrivateRoute>} />
<Route path="/employee" element={<PrivateRoute><EmployeeDashboard /></PrivateRoute>} />

View File

@ -8,6 +8,7 @@ body, html {
/* color: #fff; */
}
.login-page {
display: flex;
align-items: center;
@ -39,6 +40,12 @@ body, html {
align-items: center;
}
.logo {
display: block;
margin: 0 auto 20px;
width: 100px;
}
h1 {
margin-top: 0;
color: #0e355b;

View File

@ -2,6 +2,7 @@ import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import axiosInstance from '../../api/axiosConfig';
import './LoginPage.css';
import BrookslogoIcon from '../../assets/thob-data/BrookslogoIcon.svg';
const LoginPage = () => {
const [email, setEmail] = useState('');
@ -44,6 +45,7 @@ const LoginPage = () => {
<div className="flex flex-row min-h-screen justify-center items-center">
<div className="login-page flex items-center justify-center min-h-screen w-full md:w-1/2 lg:w-1/3 p-4">
<div className="w-full">
<img src={BrookslogoIcon} alt="Logo" className="logo" />
<h1>welcome back</h1>
<p>be among the first to experience 3D magic! register for private alpha.</p>
{error && <p className="error-message">{error}</p>}
@ -71,7 +73,7 @@ const LoginPage = () => {
className="input-field"
/>
<div className="forgot-password-container">
<a href="#">Forgot password?</a>
<a href="/reset-password">Forgot password?</a>
</div>
<button type="submit" className="login-button">
login

View File

@ -1,6 +1,7 @@
import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import axiosInstance from '../../api/axiosConfig';
import './ResetPasswordRequest.css';
const ResetPasswordRequest = () => {
const [email, setEmail] = useState('');

View File

@ -4,9 +4,9 @@ import Sidebar from '../sidebar/Sidebar';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faChevronLeft } from '@fortawesome/free-solid-svg-icons';
import axiosInstance from '../../api/axiosConfig';
import { styles } from '../../styles/classNames';
import '../../styles/AddEmployee.css';
const AddEmployee = () => {
const navigate = useNavigate();
const [employeeData, setEmployeeData] = useState({
@ -15,11 +15,13 @@ const AddEmployee = () => {
age: '',
gender: '',
email: '',
address: {
address_line_1: '',
address_line_2: '',
nearby_landmark: '',
pincode: '',
city_id: '',
},
mobile: '',
image: null,
});
@ -83,31 +85,31 @@ const AddEmployee = () => {
};
return (
<div className="flex flex-row">
<div className={styles.container}>
<Sidebar />
<div className="w-full flex flex-col min-h-screen text-black">
<div className="min-h-screen text-secondary p-6 bg-light">
<div className={styles.content}>
<div className={styles.header}>
<div className="flex items-center ">
<Link to="/employees" className="text-primary text-xl ">
<Link to="/employees" className={styles.backButton}>
<FontAwesomeIcon icon={faChevronLeft} />
</Link>
<h1 className="text-2xl font-semibold text-primary">
<h1 className={styles.title}>
create employee account
</h1>
</div>
<div className="flex justify-center">
<div className="space-y-6 w-full max-w-4xl">
<div className="text-center">
<div className={`${styles.centerText} space-y-6 w-full max-w-4xl`}>
<div className={styles.centerText}>
<div className="inline-block relative">
<div className="bg-gray-300 w-24 h-24 rounded-full mx-auto overflow-hidden">
<div className={styles.imageWrapper}>
{imagePreview ? (
<img
src={imagePreview}
alt="Employee"
className="w-full h-full object-cover"
className={styles.imagePreview}
/>
) : (
<div className="w-full h-full flex items-center justify-center text-gray-500">
<div className={styles.noImageText}>
No image
</div>
)}
@ -122,14 +124,14 @@ const AddEmployee = () => {
<button
type="button"
onClick={() => document.getElementById('imageUpload').click()}
className="image-upload-btn"
className={styles.imageUploadButton}
>
Upload
</button>
</div>
</div>
<form onSubmit={handleAddEmployeeSubmit} className="space-y-4">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<form onSubmit={handleAddEmployeeSubmit} className={styles.form}>
<div className={styles.formGroup}>
<div>
<label className="block text-secondary font-medium">
employee firstname:
@ -139,7 +141,7 @@ const AddEmployee = () => {
name="firstname"
value={employeeData.firstname}
onChange={handleInputChange}
className="input-underline w-full p-2 focus-border"
className={styles.input}
/>
</div>
<div>
@ -151,7 +153,7 @@ const AddEmployee = () => {
name="lastname"
value={employeeData.lastname}
onChange={handleInputChange}
className="input-underline w-full p-2 focus-border"
className={styles.input}
/>
</div>
<div>
@ -163,7 +165,7 @@ const AddEmployee = () => {
name="age"
value={employeeData.age}
onChange={handleInputChange}
className="input-underline w-full p-2 focus-border"
className={styles.input}
/>
</div>
<div>
@ -174,7 +176,7 @@ const AddEmployee = () => {
name="gender"
value={employeeData.gender}
onChange={handleInputChange}
className="input-underline w-full p-2 focus-border"
className={styles.input}
>
<option value="" disabled>
select gender
@ -193,7 +195,7 @@ const AddEmployee = () => {
name="mobile"
value={employeeData.mobile}
onChange={handleInputChange}
className="input-underline w-full p-2 focus-border"
className={styles.input}
/>
</div>
<div className="md:col-span-2 lg:col-span-3">
@ -205,7 +207,7 @@ const AddEmployee = () => {
name="address_line_1"
value={employeeData.address_line_1}
onChange={handleInputChange}
className="input-underline w-full p-2 focus-border"
className={styles.input}
/>
</div>
<div className="md:col-span-2 lg:col-span-3">
@ -217,7 +219,7 @@ const AddEmployee = () => {
name="address_line_2"
value={employeeData.address_line_2}
onChange={handleInputChange}
className="input-underline w-full p-2 focus-border"
className={styles.input}
/>
</div>
<div className="md:col-span-2 lg:col-span-3">
@ -229,7 +231,7 @@ const AddEmployee = () => {
name="nearby_landmark"
value={employeeData.nearby_landmark}
onChange={handleInputChange}
className="input-underline w-full p-2 focus-border"
className={styles.input}
/>
</div>
<div className="md:col-span-2 lg:col-span-3">
@ -241,7 +243,7 @@ const AddEmployee = () => {
name="pincode"
value={employeeData.pincode}
onChange={handleInputChange}
className="input-underline w-full p-2 focus-border"
className={styles.input}
/>
</div>
<div className="md:col-span-2 lg:col-span-3">
@ -252,7 +254,7 @@ const AddEmployee = () => {
name="city_id"
value={employeeData.city_id}
onChange={handleInputChange}
className="input-underline w-full p-2 focus-border"
className={styles.input}
>
<option value="" disabled>
select city
@ -274,14 +276,14 @@ const AddEmployee = () => {
name="email"
value={employeeData.email}
onChange={handleInputChange}
className="input-underline w-full p-2 focus-border"
className={styles.input}
/>
</div>
</div>
<div className="text-center mt-6">
<div className={`${styles.centerText} mt-6`}>
<button
type="submit"
className="btn-primary"
className={styles.button}
>
create account
</button>

View File

@ -5,6 +5,32 @@ import { faChevronLeft } from '@fortawesome/free-solid-svg-icons';
import Sidebar from '../sidebar/Sidebar';
import axiosInstance from '../../api/axiosConfig';
const CLASSES = {
container: 'flex flex-row',
mainContent: 'w-full flex flex-col min-h-screen text-black',
innerContent: 'min-h-screen text-gray-800 p-6',
header: 'flex justify-between items-center',
headerTitle: 'flex items-center',
backButton: 'text-[#0e355b] text-xl',
title: 'text-2xl font-semibold text-[#0e355b]',
skipLink: 'text-blue-900',
formGrid: 'grid grid-cols-1 md:grid-cols-3 gap-6',
imageSection: 'space-y-6',
imageWrapper: 'text-center',
imageContainer: 'inline-block relative',
imagePreview: 'bg-gray-300 w-24 h-24 rounded-full mx-auto overflow-hidden',
noImage: 'w-full h-full flex items-center justify-center text-gray-500',
hiddenInput: 'hidden',
uploadButton:
'absolute bottom-0 left-1/2 transform -translate-x-1/2 bg-gray-700 text-white text-xs px-2 py-1 rounded-full',
linkTitle: 'text-lg font-semibold',
formSection: 'md:col-span-2 space-y-4',
formGroup: 'block text-gray-800 font-medium',
formInput: 'w-full p-2 border-b border-gray-400 focus:border-gray-700',
formSelect: 'w-full p-2 border-b border-gray-400 focus:border-gray-700',
submitButton: 'bg-[#0e355b] text-white py-2 px-4 rounded-md',
};
const AddCustomer = () => {
const navigate = useNavigate();
const [customerData, setCustomerData] = useState({
@ -13,14 +39,17 @@ const AddCustomer = () => {
age: '',
gender: '',
email: '',
mobile: '',
image: null,
address: {
address_line_1: '',
address_line_2: '',
nearby_landmark: '',
pincode: '',
city_id: '',
mobile: '',
image: null,
},
});
const [cities, setCities] = useState([]);
const [imagePreview, setImagePreview] = useState(null);
@ -43,10 +72,21 @@ const AddCustomer = () => {
const handleInputChange = (e) => {
const { name, value } = e.target;
setCustomerData({
...customerData,
setCustomerData((prevData) => ({
...prevData,
[name]: value,
});
}));
};
const handleAddressChange = (e) => {
const { name, value } = e.target;
setCustomerData((prevData) => ({
...prevData,
address: {
...prevData.address,
[name]: value,
},
}));
};
const handleImageChange = (e) => {
@ -64,15 +104,25 @@ const AddCustomer = () => {
e.preventDefault();
const formData = new FormData();
Object.keys(customerData).forEach((key) => {
if (key === 'address') {
Object.keys(customerData.address).forEach((addrKey) => {
formData.append(`address[${addrKey}]`, customerData.address[addrKey]);
});
} else {
formData.append(key, customerData[key]);
}
});
try {
const response = await axiosInstance.post('http://localhost:5000/customers', formData, {
const response = await axiosInstance.post(
'http://localhost:5000/customers',
formData,
{
headers: {
'Content-Type': 'multipart/form-data',
},
});
}
);
console.log('Raw response:', response);
console.log('Customer added successfully:', response.data);
navigate('/customers');
@ -82,26 +132,26 @@ const AddCustomer = () => {
};
return (
<div className="flex flex-row">
<div className={CLASSES.container}>
<Sidebar />
<div className="w-full flex flex-col min-h-screen text-black">
<div className="min-h-screen text-gray-800 p-6">
<div className="flex justify-between items-center">
<div className="flex items-center">
<Link to="/customers" className="text-[#0e355b] text-xl">
<div className={CLASSES.mainContent}>
<div className={CLASSES.innerContent}>
<div className={CLASSES.header}>
<div className={CLASSES.headerTitle}>
<Link to="/customers" className={CLASSES.backButton}>
<FontAwesomeIcon icon={faChevronLeft} />
</Link>
<h1 className="text-2xl font-semibold text-[#0e355b]">
Add Customer
</h1>
<h1 className={CLASSES.title}>Add Customer</h1>
</div>
<Link to="/CategoryList" className="text-blue-900">Skip for now</Link>
<Link to="/CategoryList" className={CLASSES.skipLink}>
Skip for now
</Link>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
<div className="space-y-6">
<div className="text-center">
<div className="inline-block relative">
<div className="bg-gray-300 w-24 h-24 rounded-full mx-auto overflow-hidden">
<div className={CLASSES.formGrid}>
<div className={CLASSES.imageSection}>
<div className={CLASSES.imageWrapper}>
<div className={CLASSES.imageContainer}>
<div className={CLASSES.imagePreview}>
{imagePreview ? (
<img
src={imagePreview}
@ -109,22 +159,22 @@ const AddCustomer = () => {
className="w-full h-full object-cover"
/>
) : (
<div className="w-full h-full flex items-center justify-center text-gray-500">
No Image
</div>
<div className={CLASSES.noImage}>No Image</div>
)}
</div>
<input
type="file"
accept="image/*"
className="hidden"
className={CLASSES.hiddenInput}
id="imageUpload"
onChange={handleImageChange}
/>
<button
type="button"
onClick={() => document.getElementById('imageUpload').click()}
className="absolute bottom-0 left-1/2 transform -translate-x-1/2 bg-gray-700 text-white text-xs px-2 py-1 rounded-full"
onClick={() =>
document.getElementById('imageUpload').click()
}
className={CLASSES.uploadButton}
>
Upload
</button>
@ -132,60 +182,52 @@ const AddCustomer = () => {
</div>
<div>
<Link to="/add-customer">
<h2 className="text-lg font-semibold">Customer Information</h2>
<h2 className={CLASSES.linkTitle}>Customer Information</h2>
</Link>
<Link to="/measurements">
<h2 className="text-lg font-semibold">Measurements</h2>
<h2 className={CLASSES.linkTitle}>Measurements</h2>
</Link>
</div>
</div>
<div className="md:col-span-2 space-y-4">
<div className={CLASSES.formSection}>
<form onSubmit={handleAddCustomerSubmit}>
<div>
<label className="block text-gray-800 font-medium">
First Name:
</label>
<label className={CLASSES.formGroup}>First Name:</label>
<input
type="text"
name="firstname"
value={customerData.firstname}
onChange={handleInputChange}
className="w-full p-2 border-b border-gray-400 focus:border-gray-700"
className={CLASSES.formInput}
/>
</div>
<div>
<label className="block text-gray-800 font-medium">
Last Name:
</label>
<label className={CLASSES.formGroup}>Last Name:</label>
<input
type="text"
name="lastname"
value={customerData.lastname}
onChange={handleInputChange}
className="w-full p-2 border-b border-gray-400 focus:border-gray-700"
className={CLASSES.formInput}
/>
</div>
<div>
<label className="block text-gray-800 font-medium">
Age:
</label>
<label className={CLASSES.formGroup}>Age:</label>
<input
type="number"
name="age"
value={customerData.age}
onChange={handleInputChange}
className="w-full p-2 border-b border-gray-400 focus:border-gray-700"
className={CLASSES.formInput}
/>
</div>
<div>
<label className="block text-gray-800 font-medium">
Gender:
</label>
<label className={CLASSES.formGroup}>Gender:</label>
<select
name="gender"
value={customerData.gender}
onChange={handleInputChange}
className="w-full p-2 border-b border-gray-400 focus:border-gray-700"
className={CLASSES.formSelect}
>
<option value="" disabled>
Select Gender
@ -196,74 +238,62 @@ const AddCustomer = () => {
</select>
</div>
<div>
<label className="block text-gray-800 font-medium">
Email:
</label>
<label className={CLASSES.formGroup}>Email:</label>
<input
type="email"
name="email"
value={customerData.email}
onChange={handleInputChange}
className="w-full p-2 border-b border-gray-400 focus:border-gray-700"
className={CLASSES.formInput}
/>
</div>
<div>
<label className="block text-gray-800 font-medium">
Address Line 1:
</label>
<label className={CLASSES.formGroup}>Address Line 1:</label>
<input
type="text"
name="address_line_1"
value={customerData.address_line_1}
onChange={handleInputChange}
className="w-full p-2 border-b border-gray-400 focus:border-gray-700"
value={customerData.address.address_line_1}
onChange={handleAddressChange}
className={CLASSES.formInput}
/>
</div>
<div>
<label className="block text-gray-800 font-medium">
Address Line 2:
</label>
<label className={CLASSES.formGroup}>Address Line 2:</label>
<input
type="text"
name="address_line_2"
value={customerData.address_line_2}
onChange={handleInputChange}
className="w-full p-2 border-b border-gray-400 focus:border-gray-700"
value={customerData.address.address_line_2}
onChange={handleAddressChange}
className={CLASSES.formInput}
/>
</div>
<div>
<label className="block text-gray-800 font-medium">
Nearby Landmark:
</label>
<label className={CLASSES.formGroup}>Nearby Landmark:</label>
<input
type="text"
name="nearby_landmark"
value={customerData.nearby_landmark}
onChange={handleInputChange}
className="w-full p-2 border-b border-gray-400 focus:border-gray-700"
value={customerData.address.nearby_landmark}
onChange={handleAddressChange}
className={CLASSES.formInput}
/>
</div>
<div>
<label className="block text-gray-800 font-medium">
Pincode:
</label>
<label className={CLASSES.formGroup}>Pincode:</label>
<input
type="text"
name="pincode"
value={customerData.pincode}
onChange={handleInputChange}
className="w-full p-2 border-b border-gray-400 focus:border-gray-700"
value={customerData.address.pincode}
onChange={handleAddressChange}
className={CLASSES.formInput}
/>
</div>
<div>
<label className="block text-gray-800 font-medium">
City:
</label>
<label className={CLASSES.formGroup}>City:</label>
<select
name="city_id"
value={customerData.city_id}
onChange={handleInputChange}
className="w-full p-2 border-b border-gray-400 focus:border-gray-700"
value={customerData.address.city_id}
onChange={handleAddressChange}
className={CLASSES.formSelect}
>
<option value="" disabled>
Select City
@ -277,22 +307,17 @@ const AddCustomer = () => {
</select>
</div>
<div>
<label className="block text-gray-800 font-medium">
Mobile:
</label>
<label className={CLASSES.formGroup}>Mobile:</label>
<input
type="tel"
name="mobile"
value={customerData.mobile}
onChange={handleInputChange}
className="w-full p-2 border-b border-gray-400 focus:border-gray-700"
className={CLASSES.formInput}
/>
</div>
<div className="flex justify-end">
<button
type="submit"
className="bg-[#0e355b] text-white py-2 px-4 rounded-md"
>
<button type="submit" className={CLASSES.submitButton}>
Add Customer
</button>
</div>

View File

@ -9,14 +9,14 @@ const CustomerList = () => {
const navigate = useNavigate();
useEffect(() => {
// Fetch the customers data from the JSON file
getCustomers();
}, []);
const getCustomers = async () => {
try {
const response = await axiosInstance.get('http://localhost:5000/customers');
let customers_data = response.data; // List of customers
let customers_data = response.data;
setCustomers(customers_data);
} catch (error) {
throw error;
@ -32,7 +32,7 @@ const CustomerList = () => {
<Sidebar />
<div className="flex-grow p-6 bg-gray-100">
<div className="bg-white p-6 rounded-lg shadow">
<div className="flex justify-between items-center mb-6">
<div className="flex justify-between items-center ">
<h1 className="text-2xl font-semibold text-gray-800">customers list</h1>
<button
className="bg-#0e355b text-#d1d5db px-4 py-2 rounded-md text-sm"
@ -41,9 +41,9 @@ const CustomerList = () => {
add new customer
</button>
</div>
<div className="flex items-center mb-4">
<div className="flex items-center ">
<svg
className="w-5 h-5 mr-2 text-gray-500"
className="w-5 h-5 text-gray-500"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
@ -58,7 +58,7 @@ const CustomerList = () => {
</svg>
<span className="text-sm text-gray-600">filter</span>
</div>
<p className="text-sm text-gray-600 mb-4">total customers ({customers.length})</p>
<p className="text-sm text-gray-600 ">total customers ({customers.length})</p>
<table className="w-full bg-white">
<thead>
<tr className="text-left text-xs uppercase text-gray-600 border-b">

View File

@ -1,77 +1,97 @@
/* AddCustomer.css */
.container {
display: flex;
flex-direction: row;
}
.main-content {
flex: 1;
width: 100%;
display: flex;
flex-direction: column;
min-height: 100vh;
color: #e8e8e8;
color: black;
}
.inner-content {
min-height: 100vh;
color: #4a5568; /* text-gray-800 */
padding: 1.5rem; /* p-6 */
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1.5rem;
}
.header-title {
display: flex;
align-items: center;
margin-bottom: 1.5rem;
}
.back-link {
.back-button {
color: #0e355b;
font-size: 1.25rem;
margin-right: 1rem;
font-size: 1.25rem; /* text-xl */
}
.page-title {
font-size: 1.5rem;
font-weight: 600;
.title {
font-size: 1.5rem; /* text-2xl */
font-weight: 600; /* font-semibold */
color: #0e355b;
}
.skip-link {
color: #0e355b;
color: #1e3a8a; /* text-blue-900 */
}
.sidebar-content {
.form-grid {
display: grid;
grid-template-columns: repeat(1, 1fr);
gap: 1.5rem; /* gap-6 */
}
@media (min-width: 768px) {
.form-grid {
grid-template-columns: repeat(3, 1fr); /* md:grid-cols-3 */
}
}
.image-section {
display: flex;
flex-direction: column;
gap: 1.5rem;
gap: 1.5rem; /* space-y-6 */
}
.image-upload-container {
.image-wrapper {
text-align: center;
}
.image-container {
display: inline-block;
position: relative;
width: 6rem;
height: 6rem;
margin: 0 auto;
}
.image-preview {
background-color: #d1d5db;
width: 100%;
height: 100%;
border-radius: 9999px;
background-color: #d1d5db; /* bg-gray-300 */
width: 6rem; /* w-24 */
height: 6rem; /* h-24 */
border-radius: 50%;
margin: 0 auto; /* mx-auto */
overflow: hidden;
}
.image-preview img {
.no-image {
width: 100%;
height: 100%;
object-fit: cover;
}
.no-image {
display: flex;
align-items: center;
justify-content: center;
color: #6b7280;
color: #6b7280; /* text-gray-500 */
}
.hidden-input {
display: none;
}
.upload-button {
@ -79,57 +99,50 @@
bottom: 0;
left: 50%;
transform: translateX(-50%);
background-color: #0e355b;
color: #fff;
font-size: 0.75rem;
padding: 0.5rem;
border-radius: 9999px;
background-color: #4b5563; /* bg-gray-700 */
color: white;
font-size: 0.75rem; /* text-xs */
padding: 0.25rem 0.5rem;
border-radius: 9999px; /* rounded-full */
}
.section-title {
font-size: 1.125rem;
font-weight: 600;
.link-title {
font-size: 1.125rem; /* text-lg */
font-weight: 600; /* font-semibold */
}
.form-section {
display: flex;
flex-direction: column;
gap: 1rem; /* space-y-4 */
}
.form-group {
margin-bottom: 1rem;
display: flex;
flex-direction: column;
color: #4a5568; /* text-gray-800 */
font-weight: 500; /* font-medium */
}
.form-label {
display: underline;
color: #4b5563;
font-weight: 500;
.form-input {
width: 100%;
padding: 0.5rem;
border-bottom: 1px solid #cbd5e0; /* border-gray-400 */
&:focus {
border-color: #2d3748; /* border-gray-700 */
}
}
.form-input,
.form-select {
width: 100%;
padding: 0.5rem;
border: 1px solid #d1d5db;
border-radius: 0.375rem;
border-bottom: 1px solid #cbd5e0; /* border-gray-400 */
&:focus {
border-color: #2d3748; /* border-gray-700 */
}
.form-input:focus,
.form-select:focus {
border-color: #e8e8e8;
}
.submit-button {
background-color: #0e355b;
color: #d1d5db;
color: white;
padding: 0.5rem 1rem;
border-radius: 0.375rem;
align-self: flex-end;
}
.form-row {
display: flex;
gap: 1rem;
margin-bottom: 1rem;
}
.form-row .form-group {
flex: 1;
border-radius: 0.375rem; /* rounded-md */
}

20
src/styles/classNames.js Normal file
View File

@ -0,0 +1,20 @@
export const styles = {
container: 'flex flex-row',
content: 'w-full flex flex-col min-h-screen text-black',
header: 'min-h-screen text-secondary p-6 bg-light',
backButton: 'text-primary text-xl',
title: 'text-2xl font-semibold text-primary',
imageWrapper: 'bg-gray-300 w-24 h-24 rounded-full mx-auto overflow-hidden',
imagePreview: 'w-full h-full object-cover',
noImageText: 'w-full h-full flex items-center justify-center text-gray-500',
imageUploadButton: 'image-upload-btn',
form: 'space-y-4',
input: 'input-underline w-full p-2 focus-border',
button: 'btn-primary',
formGroup: 'grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4',
centerText: 'text-center',
};

8180
yarn.lock

File diff suppressed because it is too large Load Diff