Add customer contact fields and admin API docs (refs #41)
This commit is contained in:
@@ -68,6 +68,7 @@ export const AdminLoginPage: React.FC = () => {
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
toast.dismiss();
|
||||
|
||||
if (!validateForm()) {
|
||||
return;
|
||||
|
||||
@@ -25,7 +25,7 @@ interface FormData {
|
||||
event_type: string;
|
||||
event_name: string;
|
||||
event_date: string;
|
||||
host_email: string;
|
||||
customer_email: string;
|
||||
admin_email: string;
|
||||
require_password: boolean;
|
||||
password: string;
|
||||
@@ -122,7 +122,7 @@ export const CreateEventPage: React.FC = () => {
|
||||
event_type: 'wedding',
|
||||
event_name: '',
|
||||
event_date: format(new Date(), 'yyyy-MM-dd'),
|
||||
host_email: '',
|
||||
customer_email: '',
|
||||
admin_email: '',
|
||||
require_password: true,
|
||||
password: '',
|
||||
@@ -198,10 +198,10 @@ export const CreateEventPage: React.FC = () => {
|
||||
newErrors.event_name = t('validation.eventNameRequired');
|
||||
}
|
||||
|
||||
if (!formData.host_email) {
|
||||
newErrors.host_email = t('validation.hostEmailRequired');
|
||||
} else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.host_email)) {
|
||||
newErrors.host_email = t('validation.invalidEmailFormat');
|
||||
if (!formData.customer_email) {
|
||||
newErrors.customer_email = t('validation.hostEmailRequired');
|
||||
} else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.customer_email)) {
|
||||
newErrors.customer_email = t('validation.invalidEmailFormat');
|
||||
}
|
||||
|
||||
if (!formData.admin_email) {
|
||||
@@ -245,7 +245,8 @@ export const CreateEventPage: React.FC = () => {
|
||||
event_type: formData.event_type,
|
||||
event_name: formData.event_name,
|
||||
event_date: formData.event_date,
|
||||
host_email: formData.host_email,
|
||||
customer_name: formData.customer_email.split('@')[0],
|
||||
customer_email: formData.customer_email,
|
||||
admin_email: formData.admin_email,
|
||||
require_password: formData.require_password,
|
||||
password: formData.require_password ? formData.password : undefined,
|
||||
@@ -390,15 +391,15 @@ export const CreateEventPage: React.FC = () => {
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
{/* Customer Email */}
|
||||
<div>
|
||||
<label htmlFor="host_email" className="block text-sm font-medium text-neutral-700 mb-1">
|
||||
<label htmlFor="customer_email" className="block text-sm font-medium text-neutral-700 mb-1">
|
||||
{t('events.hostEmail')}
|
||||
</label>
|
||||
<Input
|
||||
id="host_email"
|
||||
id="customer_email"
|
||||
type="email"
|
||||
value={formData.host_email}
|
||||
onChange={handleInputChange('host_email')}
|
||||
error={errors.host_email}
|
||||
value={formData.customer_email}
|
||||
onChange={handleInputChange('customer_email')}
|
||||
error={errors.customer_email}
|
||||
placeholder={t('events.hostEmailPlaceholder')}
|
||||
leftIcon={<Mail className="w-5 h-5 text-neutral-400" />}
|
||||
/>
|
||||
|
||||
@@ -27,8 +27,8 @@ interface FormData {
|
||||
event_type: string;
|
||||
event_name: string;
|
||||
event_date: string;
|
||||
host_name: string;
|
||||
host_email: string;
|
||||
customer_name: string;
|
||||
customer_email: string;
|
||||
admin_email: string;
|
||||
require_password: boolean;
|
||||
password: string;
|
||||
@@ -86,8 +86,8 @@ export const CreateEventPageEnhanced: React.FC = () => {
|
||||
event_type: 'wedding',
|
||||
event_name: '',
|
||||
event_date: new Date().toISOString().split('T')[0], // Initialize with ISO date format
|
||||
host_name: '',
|
||||
host_email: '',
|
||||
customer_name: '',
|
||||
customer_email: '',
|
||||
admin_email: '',
|
||||
require_password: true,
|
||||
password: '',
|
||||
@@ -184,14 +184,14 @@ export const CreateEventPageEnhanced: React.FC = () => {
|
||||
newErrors.event_date = t('validation.eventDateRequired');
|
||||
}
|
||||
|
||||
if (!formData.host_name) {
|
||||
newErrors.host_name = t('validation.hostNameRequired');
|
||||
if (!formData.customer_name) {
|
||||
newErrors.customer_name = t('validation.hostNameRequired');
|
||||
}
|
||||
|
||||
if (!formData.host_email) {
|
||||
newErrors.host_email = t('validation.hostEmailRequired');
|
||||
} else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.host_email)) {
|
||||
newErrors.host_email = t('validation.invalidEmailFormat');
|
||||
if (!formData.customer_email) {
|
||||
newErrors.customer_email = t('validation.hostEmailRequired');
|
||||
} else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.customer_email)) {
|
||||
newErrors.customer_email = t('validation.invalidEmailFormat');
|
||||
}
|
||||
|
||||
if (!formData.admin_email) {
|
||||
@@ -236,8 +236,8 @@ export const CreateEventPageEnhanced: React.FC = () => {
|
||||
event_type: formData.event_type,
|
||||
event_name: formData.event_name,
|
||||
event_date: formData.event_date,
|
||||
host_name: formData.host_name,
|
||||
host_email: formData.host_email,
|
||||
customer_name: formData.customer_name,
|
||||
customer_email: formData.customer_email,
|
||||
admin_email: formData.admin_email,
|
||||
require_password: formData.require_password,
|
||||
password: formData.require_password ? formData.password : undefined,
|
||||
@@ -472,9 +472,9 @@ export const CreateEventPageEnhanced: React.FC = () => {
|
||||
<Input
|
||||
label={t('events.hostName')}
|
||||
placeholder={t('events.hostNamePlaceholder')}
|
||||
value={formData.host_name}
|
||||
onChange={handleInputChange('host_name')}
|
||||
error={errors.host_name}
|
||||
value={formData.customer_name}
|
||||
onChange={handleInputChange('customer_name')}
|
||||
error={errors.customer_name}
|
||||
leftIcon={<Calendar className="w-5 h-5" />}
|
||||
/>
|
||||
|
||||
@@ -482,9 +482,9 @@ export const CreateEventPageEnhanced: React.FC = () => {
|
||||
type="email"
|
||||
label={t('events.hostEmail')}
|
||||
placeholder={t('events.hostEmailPlaceholder')}
|
||||
value={formData.host_email}
|
||||
onChange={handleInputChange('host_email')}
|
||||
error={errors.host_email}
|
||||
value={formData.customer_email}
|
||||
onChange={handleInputChange('customer_email')}
|
||||
error={errors.customer_email}
|
||||
leftIcon={<Mail className="w-5 h-5" />}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -122,7 +122,7 @@ export const EventDetailsPage: React.FC = () => {
|
||||
allow_user_uploads: boolean;
|
||||
upload_category_id: number | null;
|
||||
hero_photo_id: number | null;
|
||||
host_name: string;
|
||||
customer_name: string;
|
||||
source_mode: 'managed' | 'reference';
|
||||
external_path: string;
|
||||
require_password: boolean;
|
||||
@@ -138,7 +138,7 @@ export const EventDetailsPage: React.FC = () => {
|
||||
allow_user_uploads: false,
|
||||
upload_category_id: null,
|
||||
hero_photo_id: null,
|
||||
host_name: '',
|
||||
customer_name: '',
|
||||
source_mode: 'managed',
|
||||
external_path: '',
|
||||
require_password: true,
|
||||
@@ -282,7 +282,7 @@ export const EventDetailsPage: React.FC = () => {
|
||||
allow_user_uploads: event.allow_user_uploads || false,
|
||||
upload_category_id: event.upload_category_id || null,
|
||||
hero_photo_id: event.hero_photo_id || null,
|
||||
host_name: event.host_name || '',
|
||||
customer_name: event.customer_name || '',
|
||||
source_mode: event.source_mode === 'reference' ? 'reference' : 'managed',
|
||||
external_path: event.external_path || '',
|
||||
require_password: normalizeRequirePassword(event.require_password),
|
||||
@@ -389,8 +389,8 @@ export const EventDetailsPage: React.FC = () => {
|
||||
updateData.external_path = editForm.source_mode === 'reference'
|
||||
? externalPathToSave
|
||||
: null;
|
||||
if (editForm.host_name !== undefined && editForm.host_name !== null) {
|
||||
updateData.host_name = editForm.host_name;
|
||||
if (editForm.customer_name !== undefined && editForm.customer_name !== null) {
|
||||
updateData.customer_name = editForm.customer_name;
|
||||
}
|
||||
|
||||
if (editForm.new_password) {
|
||||
@@ -665,8 +665,8 @@ export const EventDetailsPage: React.FC = () => {
|
||||
</label>
|
||||
<Input
|
||||
type="text"
|
||||
value={editForm.host_name}
|
||||
onChange={(e) => setEditForm(prev => ({ ...prev, host_name: e.target.value }))}
|
||||
value={editForm.customer_name}
|
||||
onChange={(e) => setEditForm(prev => ({ ...prev, customer_name: e.target.value }))}
|
||||
placeholder={t('events.hostNamePlaceholder')}
|
||||
/>
|
||||
</div>
|
||||
@@ -881,14 +881,14 @@ export const EventDetailsPage: React.FC = () => {
|
||||
<div>
|
||||
<dt className="text-sm font-medium text-neutral-500">{t('events.hostName')}</dt>
|
||||
<dd className="mt-1 text-sm text-neutral-900">
|
||||
{event.host_name || <span className="text-neutral-400">{t('common.notSet')}</span>}
|
||||
{event.customer_name || <span className="text-neutral-400">{t('common.notSet')}</span>}
|
||||
</dd>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<dt className="text-sm font-medium text-neutral-500">{t('events.hostEmail')}</dt>
|
||||
<dd className="mt-1 text-sm text-neutral-900">{event.host_email}</dd>
|
||||
<dd className="mt-1 text-sm text-neutral-900">{event.customer_email}</dd>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
||||
@@ -159,7 +159,7 @@ export const EventsListPage: React.FC = () => {
|
||||
events = events.filter(e =>
|
||||
e.event_name.toLowerCase().includes(term) ||
|
||||
e.event_type.toLowerCase().includes(term) ||
|
||||
e.host_email.toLowerCase().includes(term)
|
||||
(e.customer_email || '').toLowerCase().includes(term)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -428,7 +428,7 @@ export const EventsListPage: React.FC = () => {
|
||||
<td className="px-6 py-4">
|
||||
<div>
|
||||
<p className="text-sm font-medium text-neutral-900">{event.event_name}</p>
|
||||
<p className="text-xs text-neutral-500">{event.host_email}</p>
|
||||
<p className="text-xs text-neutral-500">{event.customer_email}</p>
|
||||
<div className="mt-1">
|
||||
<span
|
||||
className={`inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-[11px] font-medium ${
|
||||
|
||||
@@ -2,16 +2,27 @@ import { api } from '../config/api';
|
||||
import type { Event } from '../types';
|
||||
import { normalizeRequirePassword } from '../utils/accessControl';
|
||||
|
||||
const normalizeEvent = (event: Event): Event => ({
|
||||
...event,
|
||||
require_password: normalizeRequirePassword((event as any)?.require_password, true),
|
||||
});
|
||||
const normalizeEvent = (event: Event): Event => {
|
||||
const legacyHostName = (event as any)?.host_name;
|
||||
const legacyHostEmail = (event as any)?.host_email;
|
||||
|
||||
const customerName = event.customer_name ?? legacyHostName ?? undefined;
|
||||
const customerEmail = event.customer_email ?? legacyHostEmail ?? '';
|
||||
|
||||
return {
|
||||
...event,
|
||||
customer_name: customerName,
|
||||
customer_email: customerEmail,
|
||||
require_password: normalizeRequirePassword((event as any)?.require_password, true),
|
||||
};
|
||||
};
|
||||
|
||||
interface CreateEventData {
|
||||
event_type: string;
|
||||
event_name: string;
|
||||
event_date: string;
|
||||
host_email: string;
|
||||
customer_name?: string;
|
||||
customer_email: string;
|
||||
admin_email: string;
|
||||
require_password?: boolean;
|
||||
password?: string;
|
||||
@@ -33,7 +44,8 @@ interface CreateEventData {
|
||||
interface UpdateEventData {
|
||||
event_name?: string;
|
||||
event_date?: string;
|
||||
host_email?: string;
|
||||
customer_name?: string;
|
||||
customer_email?: string;
|
||||
admin_email?: string;
|
||||
require_password?: boolean;
|
||||
password?: string;
|
||||
|
||||
@@ -5,8 +5,8 @@ export interface Event {
|
||||
event_type: string;
|
||||
event_name: string;
|
||||
event_date: string;
|
||||
host_name?: string;
|
||||
host_email: string;
|
||||
customer_name?: string;
|
||||
customer_email: string;
|
||||
admin_email: string;
|
||||
welcome_message?: string;
|
||||
color_theme?: string;
|
||||
|
||||
Reference in New Issue
Block a user