fix events time issue

This commit is contained in:
chark1es 2025-05-28 01:42:24 -07:00
parent b5e9e599aa
commit 0c2fd1a8c2
5 changed files with 314 additions and 27 deletions

View file

@ -0,0 +1,89 @@
---
import { Icon } from "astro-icon/components";
import EmailRequestSettings from "./SettingsSection/EmailRequestSettings";
// Import environment variables for debugging if needed
const logtoApiEndpoint = import.meta.env.LOGTO_API_ENDPOINT || "";
---
<div id="officer-email-section" class="">
<div class="mb-6">
<h2 class="text-2xl font-bold">IEEE Email Management</h2>
<p class="opacity-70">Manage your official IEEE UCSD email address</p>
</div>
<!-- IEEE Email Management Card -->
<div
class="card bg-card shadow-xl border border-border hover:border-primary transition-all duration-300 hover:-translate-y-1 transform mb-6"
>
<div class="card-body">
<h3 class="card-title flex items-center gap-3">
<div
class="inline-flex items-center justify-center p-3 rounded-full bg-primary text-primary-foreground"
>
<Icon name="heroicons:envelope" class="h-5 w-5" />
</div>
IEEE Email Address
</h3>
<p class="text-sm opacity-70 mb-4">
Request and manage your official IEEE UCSD email address. This email can be used for official IEEE communications and professional purposes.
</p>
<div class="h-px w-full bg-border my-4"></div>
<EmailRequestSettings client:load />
</div>
</div>
<!-- Email Guidelines Card -->
<div
class="card bg-card shadow-xl border border-border hover:border-primary transition-all duration-300 hover:-translate-y-1 transform mb-6"
>
<div class="card-body">
<h3 class="card-title flex items-center gap-3">
<div
class="inline-flex items-center justify-center p-3 rounded-full bg-info text-info-foreground"
>
<Icon name="heroicons:information-circle" class="h-5 w-5" />
</div>
Email Usage Guidelines
</h3>
<div class="space-y-4 text-sm">
<div class="alert alert-info">
<Icon name="heroicons:information-circle" class="h-4 w-4" />
<div>
<h4 class="font-bold">Officer Email Access</h4>
<p>IEEE email addresses are only available to active IEEE UCSD officers. Your officer status is automatically verified when you request an email.</p>
</div>
</div>
<div class="space-y-2">
<h4 class="font-semibold">Acceptable Use:</h4>
<ul class="list-disc list-inside space-y-1 opacity-80">
<li>Official IEEE UCSD communications</li>
<li>Professional networking related to IEEE activities</li>
<li>Event coordination and planning</li>
<li>Communications with sponsors and external partners</li>
</ul>
</div>
<div class="space-y-2">
<h4 class="font-semibold">Email Features:</h4>
<ul class="list-disc list-inside space-y-1 opacity-80">
<li>Webmail access at <a href="https://mail.ieeeucsd.org" target="_blank" rel="noopener noreferrer" class="link link-primary">https://mail.ieeeucsd.org</a></li>
<li>IMAP/SMTP support for email clients</li>
<li>5GB storage space</li>
<li>Professional @ieeeucsd.org domain</li>
</ul>
</div>
<div class="space-y-2">
<h4 class="font-semibold">Important Notes:</h4>
<ul class="list-disc list-inside space-y-1 opacity-80">
<li>Your email username is based on your personal email address</li>
<li>Passwords can be reset through this interface</li>
<li>Email access may be revoked when officer status changes</li>
<li>Contact the webmaster for any technical issues</li>
</ul>
</div>
</div>
</div>
</div>
</div>

View file

@ -129,7 +129,27 @@ const EventDetailsSection: React.FC<EventDetailsSectionProps> = ({ formData, onD
type="datetime-local"
className="input input-bordered focus:input-primary transition-all duration-300 mt-2"
value={formData.start_date_time}
onChange={(e) => onDataChange({ start_date_time: e.target.value })}
onChange={(e) => {
const newStartDateTime = e.target.value;
onDataChange({ start_date_time: newStartDateTime });
// If there's already an end time set, update it to use the new start date
if (formData.end_date_time && newStartDateTime) {
try {
const existingEndDate = new Date(formData.end_date_time);
const newStartDate = new Date(newStartDateTime);
if (!isNaN(existingEndDate.getTime()) && !isNaN(newStartDate.getTime())) {
// Keep the same time but update to the new date
const updatedEndDate = new Date(newStartDate);
updatedEndDate.setHours(existingEndDate.getHours(), existingEndDate.getMinutes(), 0, 0);
onDataChange({ end_date_time: updatedEndDate.toISOString() });
}
} catch (error) {
console.error('Error updating end date when start date changed:', error);
}
}
}}
required
whileHover="hover"
variants={inputHoverVariants}
@ -155,25 +175,59 @@ const EventDetailsSection: React.FC<EventDetailsSectionProps> = ({ formData, onD
<motion.input
type="time"
className="input input-bordered focus:input-primary transition-all duration-300"
value={formData.end_date_time ? new Date(formData.end_date_time).toTimeString().substring(0, 5) : ''}
value={formData.end_date_time ? (() => {
try {
const endDate = new Date(formData.end_date_time);
if (isNaN(endDate.getTime())) return '';
return endDate.toTimeString().substring(0, 5);
} catch (e) {
return '';
}
})() : ''}
onChange={(e) => {
if (formData.start_date_time) {
const timeValue = e.target.value;
if (timeValue && formData.start_date_time) {
try {
// Create a new date object from start_date_time
const startDate = new Date(formData.start_date_time);
if (isNaN(startDate.getTime())) {
console.error('Invalid start date time');
return;
}
// Parse the time value
const [hours, minutes] = e.target.value.split(':').map(Number);
// Set the hours and minutes on the date
startDate.setHours(hours, minutes);
const [hours, minutes] = timeValue.split(':').map(Number);
// Validate hours and minutes
if (isNaN(hours) || isNaN(minutes) || hours < 0 || hours > 23 || minutes < 0 || minutes > 59) {
console.error('Invalid time values');
return;
}
// Create a new date with the same date as start but different time
const endDate = new Date(startDate);
endDate.setHours(hours, minutes, 0, 0);
// Update end_date_time with the new time but same date as start
onDataChange({ end_date_time: startDate.toISOString() });
onDataChange({ end_date_time: endDate.toISOString() });
} catch (error) {
console.error('Error setting end time:', error);
}
} else if (!timeValue) {
// Clear end_date_time if time is cleared
onDataChange({ end_date_time: '' });
}
}}
required
disabled={!formData.start_date_time}
whileHover="hover"
variants={inputHoverVariants}
/>
<p className="text-xs text-base-content/60">
The end time will use the same date as the start date.
{!formData.start_date_time
? "Please set the start date and time first."
: "The end time will use the same date as the start date."
}
</p>
</div>
</motion.div>

View file

@ -176,9 +176,28 @@ const EventRequestForm: React.FC = () => {
}
setFormData(prevData => {
// Save to localStorage
const updatedData = { ...prevData, ...sectionData };
localStorage.setItem('eventRequestFormData', JSON.stringify(updatedData));
// Save to localStorage
try {
const dataToStore = {
...updatedData,
// Remove file objects before saving to localStorage
other_logos: [],
room_booking: null,
invoice: null,
invoice_files: []
};
localStorage.setItem('eventRequestFormData', JSON.stringify(dataToStore));
// Also update the preview data
window.dispatchEvent(new CustomEvent('formDataUpdated', {
detail: { formData: updatedData }
}));
} catch (error) {
console.error('Error saving form data to localStorage:', error);
}
return updatedData;
});
};
@ -267,8 +286,36 @@ const EventRequestForm: React.FC = () => {
requested_user: userId,
name: formData.name,
location: formData.location,
start_date_time: new Date(formData.start_date_time).toISOString(),
end_date_time: formData.end_date_time ? new Date(formData.end_date_time).toISOString() : new Date(formData.start_date_time).toISOString(),
start_date_time: (() => {
try {
const startDate = new Date(formData.start_date_time);
if (isNaN(startDate.getTime())) {
throw new Error('Invalid start date');
}
return startDate.toISOString();
} catch (e) {
throw new Error('Invalid start date format');
}
})(),
end_date_time: (() => {
try {
if (formData.end_date_time) {
const endDate = new Date(formData.end_date_time);
if (isNaN(endDate.getTime())) {
throw new Error('Invalid end date');
}
return endDate.toISOString();
} else {
// Fallback to start date if no end date (should not happen with validation)
const startDate = new Date(formData.start_date_time);
return startDate.toISOString();
}
} catch (e) {
// Fallback to start date
const startDate = new Date(formData.start_date_time);
return startDate.toISOString();
}
})(),
event_description: formData.event_description,
flyers_needed: formData.flyers_needed,
photography_needed: formData.photography_needed,
@ -277,7 +324,14 @@ const EventRequestForm: React.FC = () => {
itemized_invoice: formData.itemized_invoice,
flyer_type: formData.flyer_type,
other_flyer_type: formData.other_flyer_type,
flyer_advertising_start_date: formData.flyer_advertising_start_date ? new Date(formData.flyer_advertising_start_date).toISOString() : '',
flyer_advertising_start_date: formData.flyer_advertising_start_date ? (() => {
try {
const advertDate = new Date(formData.flyer_advertising_start_date);
return isNaN(advertDate.getTime()) ? '' : advertDate.toISOString();
} catch (e) {
return '';
}
})() : '',
flyer_additional_requests: formData.flyer_additional_requests,
required_logos: formData.required_logos,
advertising_format: formData.advertising_format,
@ -407,11 +461,47 @@ const EventRequestForm: React.FC = () => {
if (!formData.start_date_time || formData.start_date_time.trim() === '') {
errors.push('Event start date and time is required');
valid = false;
} else {
// Validate start date format
try {
const startDate = new Date(formData.start_date_time);
if (isNaN(startDate.getTime())) {
errors.push('Invalid start date and time format');
valid = false;
} else {
// Check if start date is in the future
const now = new Date();
if (startDate <= now) {
errors.push('Event start date must be in the future');
valid = false;
}
}
} catch (e) {
errors.push('Invalid start date and time format');
valid = false;
}
}
if (!formData.end_date_time) {
if (!formData.end_date_time || formData.end_date_time.trim() === '') {
errors.push('Event end time is required');
valid = false;
} else if (formData.start_date_time) {
// Validate end date format and logic
try {
const startDate = new Date(formData.start_date_time);
const endDate = new Date(formData.end_date_time);
if (isNaN(endDate.getTime())) {
errors.push('Invalid end date and time format');
valid = false;
} else if (!isNaN(startDate.getTime()) && endDate <= startDate) {
errors.push('Event end time must be after the start time');
valid = false;
}
} catch (e) {
errors.push('Invalid end date and time format');
valid = false;
}
}
if (!formData.location || formData.location.trim() === '') {
@ -419,7 +509,7 @@ const EventRequestForm: React.FC = () => {
valid = false;
}
if (formData.will_or_have_room_booking === undefined) {
if (formData.will_or_have_room_booking === undefined || formData.will_or_have_room_booking === null) {
errors.push('Room booking status is required');
valid = false;
}

View file

@ -1644,6 +1644,11 @@ const EventRequestDetails = ({
<label className="text-xs text-gray-400">Start Date & Time</label>
<p className="text-white">{formatDate(request.start_date_time)}</p>
</div>
<div>
<label className="text-xs text-gray-400">End Date & Time</label>
<p className="text-white">{formatDate(request.end_date_time)}</p>
</div>
</div>
</div>

View file

@ -231,6 +231,50 @@ const EventRequestManagementTable = ({
}
};
// Format date and time range for display
const formatDateTimeRange = (startDateString: string, endDateString: string) => {
if (!startDateString) return 'Not specified';
try {
const startDate = new Date(startDateString);
const endDate = endDateString ? new Date(endDateString) : null;
const startFormatted = startDate.toLocaleDateString('en-US', {
month: 'short',
day: 'numeric',
hour: '2-digit',
minute: '2-digit'
});
if (endDate && endDate.getTime() !== startDate.getTime()) {
// Check if it's the same day
const isSameDay = startDate.toDateString() === endDate.toDateString();
if (isSameDay) {
// Same day, just show end time
const endTime = endDate.toLocaleTimeString('en-US', {
hour: '2-digit',
minute: '2-digit'
});
return `${startFormatted} - ${endTime}`;
} else {
// Different day, show full end date
const endFormatted = endDate.toLocaleDateString('en-US', {
month: 'short',
day: 'numeric',
hour: '2-digit',
minute: '2-digit'
});
return `${startFormatted} - ${endFormatted}`;
}
}
return startFormatted;
} catch (e) {
return startDateString;
}
};
// Get status badge class based on status
const getStatusBadge = (status?: "submitted" | "pending" | "completed" | "declined") => {
if (!status) return 'badge-warning';
@ -489,7 +533,7 @@ const EventRequestManagementTable = ({
height: "auto"
}}
>
<table className="table table-zebra w-full">
<table className="table table-zebra w-full min-w-[600px]">
<thead className="bg-base-300/50 sticky top-0 z-10">
<tr>
<th
@ -510,7 +554,7 @@ const EventRequestManagementTable = ({
onClick={() => handleSortChange('start_date_time')}
>
<div className="flex items-center gap-1">
Date
Date & Time
{sortField === 'start_date_time' && (
<svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4 text-primary" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d={sortDirection === 'asc' ? "M5 15l7-7 7 7" : "M19 9l-7 7-7-7"} />
@ -559,7 +603,7 @@ const EventRequestManagementTable = ({
)}
</div>
</th>
<th>Actions</th>
<th className="w-20 min-w-[5rem]">Actions</th>
</tr>
</thead>
<tbody>
@ -570,7 +614,11 @@ const EventRequestManagementTable = ({
{truncateText(request.name, 30)}
</div>
</td>
<td className="hidden md:table-cell">{formatDate(request.start_date_time)}</td>
<td className="hidden md:table-cell">
<div className="text-sm">
{formatDateTimeRange(request.start_date_time, request.end_date_time)}
</div>
</td>
<td>
{(() => {
const { name, email } = getUserDisplayInfo(request);
@ -603,16 +651,17 @@ const EventRequestManagementTable = ({
</span>
</td>
<td>
<div className="flex items-center gap-2">
<div className="flex items-center justify-center">
<button
className="btn btn-sm btn-primary btn-outline btn-sm gap-2"
className="btn btn-sm btn-primary btn-outline gap-1 min-h-[2rem] max-w-[5rem] flex-shrink-0"
onClick={() => openDetailModal(request)}
title="View Event Details"
>
<svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<svg xmlns="http://www.w3.org/2000/svg" className="h-3 w-3 flex-shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
</svg>
View
<span className="hidden sm:inline">View</span>
</button>
</div>
</td>