Fix hydration
This commit is contained in:
parent
74b76ee053
commit
eb77c00540
3 changed files with 30 additions and 46 deletions
|
@ -4,6 +4,7 @@ import { Get } from "../../scripts/pocketbase/Get";
|
|||
import EventRequestForm from "./Officer_EventRequestForm/EventRequestForm";
|
||||
import UserEventRequests from "./Officer_EventRequestForm/UserEventRequests";
|
||||
import { Collections } from "../../schemas/pocketbase/schema";
|
||||
import { Toaster } from "react-hot-toast";
|
||||
|
||||
// Import the EventRequest type from UserEventRequests to ensure consistency
|
||||
import type { EventRequest as UserEventRequest } from "./Officer_EventRequestForm/UserEventRequests";
|
||||
|
@ -117,6 +118,9 @@ if (auth.isAuthenticated()) {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Toast container for notifications -->
|
||||
<Toaster client:load position="bottom-right" />
|
||||
|
||||
<script>
|
||||
// Import the DataSyncService for client-side use
|
||||
import { DataSyncService } from "../../scripts/database/DataSyncService";
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
import toast, { Toaster } from 'react-hot-toast';
|
||||
import toast from 'react-hot-toast';
|
||||
import { Authentication } from '../../../scripts/pocketbase/Authentication';
|
||||
import { Update } from '../../../scripts/pocketbase/Update';
|
||||
import { FileManager } from '../../../scripts/pocketbase/FileManager';
|
||||
|
@ -774,28 +774,6 @@ const EventRequestForm: React.FC = () => {
|
|||
|
||||
return (
|
||||
<>
|
||||
<Toaster
|
||||
position="top-right"
|
||||
toastOptions={{
|
||||
duration: 4000,
|
||||
style: {
|
||||
background: '#333',
|
||||
color: '#fff',
|
||||
},
|
||||
success: {
|
||||
duration: 3000,
|
||||
style: {
|
||||
background: 'green',
|
||||
},
|
||||
},
|
||||
error: {
|
||||
duration: 5000,
|
||||
style: {
|
||||
background: 'red',
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<motion.div
|
||||
variants={containerVariants}
|
||||
initial="hidden"
|
||||
|
|
|
@ -50,6 +50,16 @@ const InvoiceBuilder: React.FC<InvoiceBuilderProps> = ({ invoiceData, onChange }
|
|||
unitPrice: 0
|
||||
});
|
||||
|
||||
// Use a counter for generating IDs to avoid hydration issues
|
||||
const [idCounter, setIdCounter] = useState(1);
|
||||
|
||||
// Generate a unique ID for new items without using non-deterministic functions
|
||||
const generateId = () => {
|
||||
const id = `item-${idCounter}`;
|
||||
setIdCounter(prev => prev + 1);
|
||||
return id;
|
||||
};
|
||||
|
||||
// State for validation errors
|
||||
const [errors, setErrors] = useState<{
|
||||
description?: string;
|
||||
|
@ -58,11 +68,6 @@ const InvoiceBuilder: React.FC<InvoiceBuilderProps> = ({ invoiceData, onChange }
|
|||
vendor?: string;
|
||||
}>({});
|
||||
|
||||
// Generate a unique ID for new items
|
||||
const generateId = () => {
|
||||
return Date.now().toString(36) + Math.random().toString(36).substring(2);
|
||||
};
|
||||
|
||||
// Calculate totals whenever invoice data changes
|
||||
useEffect(() => {
|
||||
calculateTotals();
|
||||
|
@ -92,12 +97,13 @@ const InvoiceBuilder: React.FC<InvoiceBuilderProps> = ({ invoiceData, onChange }
|
|||
});
|
||||
};
|
||||
|
||||
// Validate new item
|
||||
// Validate new item before adding
|
||||
const validateNewItem = () => {
|
||||
const newErrors: {
|
||||
description?: string;
|
||||
quantity?: string;
|
||||
unitPrice?: string;
|
||||
vendor?: string;
|
||||
} = {};
|
||||
|
||||
if (!newItem.description.trim()) {
|
||||
|
@ -112,24 +118,22 @@ const InvoiceBuilder: React.FC<InvoiceBuilderProps> = ({ invoiceData, onChange }
|
|||
newErrors.unitPrice = 'Unit price must be 0 or greater';
|
||||
}
|
||||
|
||||
setErrors(newErrors);
|
||||
return Object.keys(newErrors).length === 0;
|
||||
};
|
||||
|
||||
// Add a new item
|
||||
const handleAddItem = () => {
|
||||
if (!validateNewItem()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for duplicate description
|
||||
const isDuplicate = invoiceData.items.some(
|
||||
item => item.description.toLowerCase() === newItem.description.toLowerCase()
|
||||
);
|
||||
|
||||
if (isDuplicate) {
|
||||
setErrors({ description: 'An item with this description already exists' });
|
||||
toast.error('An item with this description already exists');
|
||||
newErrors.description = 'An item with this description already exists';
|
||||
}
|
||||
|
||||
setErrors(newErrors);
|
||||
return Object.keys(newErrors).length === 0;
|
||||
};
|
||||
|
||||
// Add a new item to the invoice
|
||||
const handleAddItem = () => {
|
||||
if (!validateNewItem()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -320,17 +324,14 @@ const InvoiceBuilder: React.FC<InvoiceBuilderProps> = ({ invoiceData, onChange }
|
|||
</label>
|
||||
<input
|
||||
type="number"
|
||||
id="quantity"
|
||||
className={`input input-bordered input-sm ${errors.quantity ? 'input-error' : ''}`}
|
||||
value={newItem.quantity}
|
||||
onChange={(e) => setNewItem({ ...newItem, quantity: parseInt(e.target.value) || 0 })}
|
||||
min="1"
|
||||
step="1"
|
||||
/>
|
||||
{errors.quantity && (
|
||||
<label className="label">
|
||||
<span className="label-text-alt text-error">{errors.quantity}</span>
|
||||
</label>
|
||||
)}
|
||||
{errors.quantity && <div className="text-error text-xs mt-1">{errors.quantity}</div>}
|
||||
</div>
|
||||
<div className="form-control">
|
||||
<label className="label">
|
||||
|
@ -338,6 +339,7 @@ const InvoiceBuilder: React.FC<InvoiceBuilderProps> = ({ invoiceData, onChange }
|
|||
</label>
|
||||
<input
|
||||
type="number"
|
||||
id="unitPrice"
|
||||
className={`input input-bordered input-sm ${errors.unitPrice ? 'input-error' : ''}`}
|
||||
value={newItem.unitPrice}
|
||||
onChange={(e) => setNewItem({ ...newItem, unitPrice: parseFloat(e.target.value) || 0 })}
|
||||
|
|
Loading…
Reference in a new issue