@diagramers/admin
Version:
Diagramers Admin Template - React starter for admin dashboards.
311 lines (293 loc) • 11 kB
JavaScript
import React, { useState, useRef } from 'react';
import { Row, Col, Card, Form, Button, Alert } from 'react-bootstrap';
import { useFormik } from 'formik';
import * as Yup from 'yup';
import { useDispatch } from 'react-redux';
import { useLocation } from 'react-router-dom';
import { settingsChangeColor } from 'settings/settingsSlice';
import { THEME_COLOR } from 'constants.js';
import CsLineIcons from 'cs-line-icons/CsLineIcons';
import configService from 'services/configService';
import './Setup.css';
const Setup = () => {
const dispatch = useDispatch();
const location = useLocation();
const fileInputRef = useRef(null);
const [logoPreview, setLogoPreview] = useState(null);
const [isSubmitting, setIsSubmitting] = useState(false);
// Available themes with preview colors
const availableThemes = [
{
key: THEME_COLOR.LightBlue,
name: 'Light Blue',
color: '#1ea8e7',
description: 'Professional and clean blue theme'
},
{
key: THEME_COLOR.DarkBlue,
name: 'Dark Blue',
color: '#1ea8e7',
description: 'Modern dark blue theme'
},
{
key: THEME_COLOR.LightRed,
name: 'Light Red',
color: '#cf2637',
description: 'Energetic red theme'
},
{
key: THEME_COLOR.DarkRed,
name: 'Dark Red',
color: '#cf2637',
description: 'Bold dark red theme'
},
{
key: THEME_COLOR.LightGreen,
name: 'Light Green',
color: '#439b38',
description: 'Fresh green theme'
},
{
key: THEME_COLOR.DarkGreen,
name: 'Dark Green',
color: '#439b38',
description: 'Natural dark green theme'
},
{
key: THEME_COLOR.LightPurple,
name: 'Light Purple',
color: '#6f42c1',
description: 'Creative purple theme'
},
{
key: THEME_COLOR.DarkPurple,
name: 'Dark Purple',
color: '#6f42c1',
description: 'Elegant dark purple theme'
},
{
key: THEME_COLOR.LightPink,
name: 'Light Pink',
color: '#e83e8c',
description: 'Playful pink theme'
},
{
key: THEME_COLOR.DarkPink,
name: 'Dark Pink',
color: '#e83e8c',
description: 'Vibrant dark pink theme'
},
];
const validationSchema = Yup.object().shape({
projectName: Yup.string()
.min(2, 'Project name must be at least 2 characters')
.max(50, 'Project name must be less than 50 characters')
.required('Project name is required'),
defaultTheme: Yup.string()
.oneOf(Object.values(THEME_COLOR), 'Please select a valid theme')
.required('Default theme is required'),
});
const formik = useFormik({
initialValues: {
projectName: '',
defaultTheme: THEME_COLOR.LightBlue,
},
validationSchema,
onSubmit: async (values) => {
setIsSubmitting(true);
try {
// Save project configuration using ConfigService
const success = configService.completeSetup({
projectName: values.projectName,
defaultTheme: values.defaultTheme,
logo: logoPreview,
});
if (success) {
// Update theme
dispatch(settingsChangeColor(values.defaultTheme));
// Update document title
document.title = values.projectName;
// Redirect to dashboard
location.push('/');
} else {
throw new Error('Failed to save configuration');
}
} catch (error) {
console.error('Setup failed:', error);
alert('Failed to complete setup. Please try again.');
} finally {
setIsSubmitting(false);
}
},
});
const handleLogoUpload = (event) => {
const file = event.target.files[0];
if (file) {
// Validate file type
if (!file.type.startsWith('image/')) {
alert('Please select an image file');
return;
}
// Validate file size (max 2MB)
if (file.size > 2 * 1024 * 1024) {
alert('Logo file size must be less than 2MB');
return;
}
const reader = new FileReader();
reader.onload = (e) => {
setLogoPreview(e.target.result);
};
reader.readAsDataURL(file);
}
};
const removeLogo = () => {
setLogoPreview(null);
if (fileInputRef.current) {
fileInputRef.current.value = '';
}
};
return (
<div className="setup-page">
<div className="setup-container">
<Row className="justify-content-center">
<Col lg="8" xl="6">
<Card className="setup-card">
<Card.Body className="p-5">
{/* Header */}
<div className="text-center mb-5">
<div className="setup-icon mb-3">
<CsLineIcons icon="settings" className="text-primary" size="48" />
</div>
<h1 className="setup-title">Welcome to Your Admin Dashboard</h1>
<p className="setup-subtitle text-muted">
Let's configure your project settings to get started
</p>
</div>
<Form onSubmit={formik.handleSubmit}>
{/* Project Name */}
<Form.Group className="mb-4">
<Form.Label className="fw-bold">Project Name</Form.Label>
<Form.Control
type="text"
name="projectName"
placeholder="Enter your project name"
value={formik.values.projectName}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
isInvalid={formik.touched.projectName && formik.errors.projectName}
/>
<Form.Control.Feedback type="invalid">
{formik.errors.projectName}
</Form.Control.Feedback>
<Form.Text className="text-muted">
This name will be displayed in the header and browser tab
</Form.Text>
</Form.Group>
{/* Logo Upload */}
<Form.Group className="mb-4">
<Form.Label className="fw-bold">Project Logo</Form.Label>
<div className="logo-upload-area">
{logoPreview ? (
<div className="logo-preview-container">
<img
src={logoPreview}
alt="Logo preview"
className="logo-preview"
/>
<Button
variant="outline-danger"
size="sm"
onClick={removeLogo}
className="remove-logo-btn"
>
<CsLineIcons icon="trash" />
</Button>
</div>
) : (
<div
className="logo-upload-placeholder"
onClick={() => fileInputRef.current?.click()}
>
<CsLineIcons icon="upload" className="text-muted" size="32" />
<p className="mb-0 mt-2">Click to upload logo</p>
<small className="text-muted">PNG, JPG, SVG (max 2MB)</small>
</div>
)}
<input
ref={fileInputRef}
type="file"
accept="image/*"
onChange={handleLogoUpload}
style={{ display: 'none' }}
/>
</div>
<Form.Text className="text-muted">
Upload your project logo (recommended: 200x200px)
</Form.Text>
</Form.Group>
{/* Theme Selection */}
<Form.Group className="mb-5">
<Form.Label className="fw-bold">Default Theme</Form.Label>
<div className="theme-grid">
{availableThemes.map((theme) => (
<div
key={theme.key}
className={`theme-option ${formik.values.defaultTheme === theme.key ? 'selected' : ''}`}
onClick={() => formik.setFieldValue('defaultTheme', theme.key)}
>
<div
className="theme-color-preview"
style={{ backgroundColor: theme.color }}
/>
<div className="theme-info">
<h6 className="theme-name">{theme.name}</h6>
<p className="theme-description">{theme.description}</p>
</div>
{formik.values.defaultTheme === theme.key && (
<div className="theme-selected">
<CsLineIcons icon="check" className="text-white" />
</div>
)}
</div>
))}
</div>
{formik.touched.defaultTheme && formik.errors.defaultTheme && (
<div className="text-danger mt-2">{formik.errors.defaultTheme}</div>
)}
</Form.Group>
{/* Submit Button */}
<div className="text-center">
<Button
type="submit"
size="lg"
disabled={isSubmitting || !formik.isValid}
className="setup-submit-btn"
>
{isSubmitting ? (
<>
<span className="spinner-border spinner-border-sm me-2" />
Setting up...
</>
) : (
<>
<CsLineIcons icon="check" className="me-2" />
Complete Setup
</>
)}
</Button>
</div>
</Form>
{/* Info Alert */}
<Alert variant="info" className="mt-4">
<CsLineIcons icon="info" className="me-2" />
<strong>Note:</strong> You can change these settings later from the admin panel.
</Alert>
</Card.Body>
</Card>
</Col>
</Row>
</div>
</div>
);
};
export default Setup;