booking-flow-finalized-design kindaaaa #7
@@ -6,6 +6,7 @@ import Dropdown from '../ui/Dropdown';
|
||||
import { BookingTitleField } from '../forms/BookingTitleField';
|
||||
import { ParticipantsSelector } from '../forms/ParticipantsSelector';
|
||||
import { BookingProvider } from '../../context/BookingContext';
|
||||
import { Label } from '../ui/Label';
|
||||
import { PEOPLE, USER } from '../../constants/bookingConstants';
|
||||
|
||||
function BookingCard({ booking, onClick, isExpanded, onBookingUpdate, onBookingDelete }) {
|
||||
@@ -176,173 +177,176 @@ function BookingCard({ booking, onClick, isExpanded, onBookingUpdate, onBookingD
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={`${styles.card} ${isExpanded ? styles.expanded : ''}`}>
|
||||
<div className={styles.header} onClick={!isExpanded ? onClick : undefined}>
|
||||
<div className={styles.topSection}>
|
||||
<div className={styles.titleRow}>
|
||||
<h3 className={styles.title}>{booking.title}</h3>
|
||||
</div>
|
||||
<div className={styles.time}>
|
||||
{getTimeFromIndex(booking.startTime)} – {getTimeFromIndex(calculatedEndTime || booking.endTime)}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div className={styles.bottomSection}>
|
||||
{isParticipantBooking && booking.createdBy && (
|
||||
<div className={styles.createdBy}>
|
||||
Tillagd av {booking.createdBy.name}
|
||||
<div className={`${styles.cardWrapper} ${isExpanded ? styles.expanded : ''}`}>
|
||||
<div className={styles.card}>
|
||||
<div className={styles.header} onClick={!isExpanded ? onClick : undefined}>
|
||||
<div className={styles.topSection}>
|
||||
<div className={styles.titleRow}>
|
||||
<h3 className={styles.title}>{booking.title}</h3>
|
||||
</div>
|
||||
<div className={styles.time}>
|
||||
{getTimeFromIndex(booking.startTime)} – {getTimeFromIndex(calculatedEndTime || booking.endTime)}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div className={styles.bottomSection}>
|
||||
{booking.participants && booking.participants.length > 0 && (
|
||||
<div className={styles.participants}>{formatParticipants(booking.participants)}</div>
|
||||
)}
|
||||
<div className={styles.roomBadge}>
|
||||
{booking.room}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!isParticipantBooking && booking.participants && booking.participants.length > 0 && (
|
||||
<div className={styles.participants}>{formatParticipants(booking.participants)}</div>
|
||||
)}
|
||||
<div className={styles.roomBadge}>
|
||||
{booking.room}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{isExpanded && (
|
||||
<BookingProvider value={localBookingContext}>
|
||||
{isExpanded && (
|
||||
<div className={styles.expandedContent}>
|
||||
{isParticipantBooking ? (
|
||||
// Participant booking view - read-only with remove self option
|
||||
<>
|
||||
<div className={styles.readOnlySection}>
|
||||
<div className={styles.readOnlyField}>
|
||||
<label className={styles.label}>Bokning skapad av</label>
|
||||
<p className={styles.createdByText}>{booking.createdBy?.name}</p>
|
||||
</div>
|
||||
<div className={styles.readOnlyField}>
|
||||
<label className={styles.label}>Deltagare</label>
|
||||
<p className={styles.participantsText}>
|
||||
{booking.participants
|
||||
?.filter(p => p.id !== booking.createdBy?.id)
|
||||
?.map(p => p.name)
|
||||
?.join(', ')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{!showDeleteConfirm ? (
|
||||
<div className={styles.buttonSection}>
|
||||
<Button
|
||||
className={styles.deleteButton}
|
||||
onPress={() => setShowDeleteConfirm(true)}
|
||||
>
|
||||
Lämna bokning
|
||||
</Button>
|
||||
<Button
|
||||
className={styles.cancelButton}
|
||||
onPress={handleCancel}
|
||||
>
|
||||
Stäng
|
||||
</Button>
|
||||
</div>
|
||||
) : (
|
||||
<div className={styles.confirmationSection}>
|
||||
<div className={styles.confirmationMessage}>
|
||||
<span className={styles.warningIcon}>⚠️</span>
|
||||
<p>Är du säker på att du vill lämna denna bokning?</p>
|
||||
<p className={styles.bookingDetails}>
|
||||
Du kommer inte längre att vara med på "{booking.title}" den {booking.date.day}/{booking.date.month}
|
||||
<BookingProvider value={localBookingContext}>
|
||||
{isParticipantBooking ? (
|
||||
// Participant booking view - read-only with remove self option
|
||||
<>
|
||||
<div className={styles.readOnlySection}>
|
||||
<div className={styles.readOnlyField}>
|
||||
<Label>Bokning skapad av</Label>
|
||||
<p className={styles.createdByText}>{booking.createdBy?.name}</p>
|
||||
</div>
|
||||
<div className={styles.readOnlyField}>
|
||||
<Label>Deltagare</Label>
|
||||
<p className={styles.participantsText}>
|
||||
{booking.participants
|
||||
?.filter(p => p.id !== booking.createdBy?.id)
|
||||
?.map(p => p.name)
|
||||
?.join(', ')}
|
||||
</p>
|
||||
</div>
|
||||
<div className={styles.confirmationButtons}>
|
||||
</div>
|
||||
|
||||
{!showDeleteConfirm ? (
|
||||
<div className={styles.buttonSection}>
|
||||
<Button
|
||||
className={styles.confirmDeleteButton}
|
||||
onPress={handleRemoveSelf}
|
||||
className={styles.deleteButton}
|
||||
onPress={() => setShowDeleteConfirm(true)}
|
||||
>
|
||||
Ja, lämna bokning
|
||||
Lämna bokning
|
||||
</Button>
|
||||
<Button
|
||||
className={styles.cancelDeleteButton}
|
||||
onPress={cancelDelete}
|
||||
className={styles.cancelButton}
|
||||
onPress={handleCancel}
|
||||
>
|
||||
Stäng
|
||||
</Button>
|
||||
</div>
|
||||
) : (
|
||||
<div className={styles.confirmationSection}>
|
||||
<div className={styles.confirmationMessage}>
|
||||
<span className={styles.warningIcon}>⚠️</span>
|
||||
<p>Är du säker på att du vill lämna denna bokning?</p>
|
||||
<p className={styles.bookingDetails}>
|
||||
Du kommer inte längre att vara med på "{booking.title}" den {booking.date.day}/{booking.date.month}
|
||||
</p>
|
||||
</div>
|
||||
<div className={styles.confirmationButtons}>
|
||||
<Button
|
||||
className={styles.confirmDeleteButton}
|
||||
onPress={handleRemoveSelf}
|
||||
>
|
||||
Ja, lämna bokning
|
||||
</Button>
|
||||
<Button
|
||||
className={styles.cancelDeleteButton}
|
||||
onPress={cancelDelete}
|
||||
>
|
||||
Avbryt
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
// Regular booking view - editable
|
||||
<>
|
||||
<div className={styles.formSection}>
|
||||
<BookingTitleField compact={true} />
|
||||
</div>
|
||||
|
||||
<div className={styles.formSection}>
|
||||
<ParticipantsSelector compact={true} />
|
||||
</div>
|
||||
|
||||
<div className={styles.editSection}>
|
||||
<Label>Ändra längd</Label>
|
||||
<Dropdown
|
||||
options={bookingLengths}
|
||||
disabledOptions={disabledOptions}
|
||||
onChange={handleLengthChange}
|
||||
value={selectedLength || ""}
|
||||
placeholder={{
|
||||
value: "",
|
||||
label: "Välj bokningslängd"
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{!showDeleteConfirm ? (
|
||||
<div className={styles.buttonSection}>
|
||||
<Button
|
||||
className={styles.deleteButton}
|
||||
onPress={handleDelete}
|
||||
>
|
||||
Radera
|
||||
</Button>
|
||||
<Button
|
||||
className={styles.cancelButton}
|
||||
onPress={handleCancel}
|
||||
>
|
||||
Avbryt
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
// Regular booking view - editable
|
||||
<>
|
||||
<div className={styles.formSection}>
|
||||
<BookingTitleField compact={true} />
|
||||
</div>
|
||||
|
||||
<div className={styles.formSection}>
|
||||
<ParticipantsSelector compact={true} />
|
||||
</div>
|
||||
|
||||
<div className={styles.editSection}>
|
||||
<label className={styles.label}>Ändra längd</label>
|
||||
<Dropdown
|
||||
options={bookingLengths}
|
||||
disabledOptions={disabledOptions}
|
||||
onChange={handleLengthChange}
|
||||
value={selectedLength || ""}
|
||||
placeholder={{
|
||||
value: "",
|
||||
label: "Välj bokningslängd"
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{!showDeleteConfirm ? (
|
||||
<div className={styles.buttonSection}>
|
||||
<Button
|
||||
className={styles.deleteButton}
|
||||
onPress={handleDelete}
|
||||
>
|
||||
Radera
|
||||
</Button>
|
||||
<Button
|
||||
className={styles.cancelButton}
|
||||
onPress={handleCancel}
|
||||
>
|
||||
Avbryt
|
||||
</Button>
|
||||
<Button
|
||||
className={`${styles.saveButton} ${selectedLength === null ? styles.disabledButton : ''}`}
|
||||
onPress={handleSave}
|
||||
isDisabled={selectedLength === null}
|
||||
>
|
||||
{selectedLength !== null ? 'Spara ändringar' : 'Välj längd först'}
|
||||
</Button>
|
||||
</div>
|
||||
) : (
|
||||
<div className={styles.confirmationSection}>
|
||||
<div className={styles.confirmationMessage}>
|
||||
<span className={styles.warningIcon}>⚠️</span>
|
||||
<p>Är du säker på att du vill radera denna bokning?</p>
|
||||
<p className={styles.bookingDetails}>
|
||||
"{booking.title}" den {booking.date.day}/{booking.date.month} kl. {getTimeFromIndex(booking.startTime)}
|
||||
</p>
|
||||
</div>
|
||||
<div className={styles.confirmationButtons}>
|
||||
<Button
|
||||
className={styles.confirmDeleteButton}
|
||||
onPress={confirmDelete}
|
||||
className={`${styles.saveButton} ${selectedLength === null ? styles.disabledButton : ''}`}
|
||||
onPress={handleSave}
|
||||
isDisabled={selectedLength === null}
|
||||
>
|
||||
Ja, radera
|
||||
</Button>
|
||||
<Button
|
||||
className={styles.cancelDeleteButton}
|
||||
onPress={cancelDelete}
|
||||
>
|
||||
Avbryt
|
||||
{selectedLength !== null ? 'Spara ändringar' : 'Välj längd först'}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
) : (
|
||||
<div className={styles.confirmationSection}>
|
||||
<div className={styles.confirmationMessage}>
|
||||
<span className={styles.warningIcon}>⚠️</span>
|
||||
<p>Är du säker på att du vill radera denna bokning?</p>
|
||||
<p className={styles.bookingDetails}>
|
||||
"{booking.title}" den {booking.date.day}/{booking.date.month} kl. {getTimeFromIndex(booking.startTime)}
|
||||
</p>
|
||||
</div>
|
||||
<div className={styles.confirmationButtons}>
|
||||
<Button
|
||||
className={styles.confirmDeleteButton}
|
||||
onPress={confirmDelete}
|
||||
>
|
||||
Ja, radera
|
||||
</Button>
|
||||
<Button
|
||||
className={styles.cancelDeleteButton}
|
||||
onPress={cancelDelete}
|
||||
>
|
||||
Avbryt
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</BookingProvider>
|
||||
</div>
|
||||
</BookingProvider>
|
||||
)}
|
||||
)}
|
||||
|
||||
</div>
|
||||
{isParticipantBooking && booking.createdBy && !isExpanded && (
|
||||
<div className={styles.banner}>
|
||||
Tillagd av {booking.createdBy.name}
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
.cardWrapper {
|
||||
width: 100%;
|
||||
transition: var(--transition-medium);
|
||||
}
|
||||
|
||||
.card {
|
||||
border: var(--border-width-thin) solid var(--border-light);
|
||||
padding: 0.8rem 1rem;
|
||||
@@ -7,7 +12,7 @@
|
||||
}
|
||||
|
||||
@media (hover: hover) {
|
||||
.card:hover {
|
||||
.cardWrapper:hover .card {
|
||||
cursor: pointer;
|
||||
border-color: var(--color-primary);
|
||||
box-shadow: var(--shadow-xl);
|
||||
@@ -15,11 +20,22 @@
|
||||
}
|
||||
}
|
||||
|
||||
.banner {
|
||||
background-color: #f3f4f6;
|
||||
color: #6b7280;
|
||||
font-size: 0.8rem;
|
||||
padding: 0.25rem 1rem;
|
||||
border-left: var(--border-width-thin) solid var(--border-light);
|
||||
border-right: var(--border-width-thin) solid var(--border-light);
|
||||
border-bottom: var(--border-width-thin) solid var(--border-light);
|
||||
/*border-bottom-left-radius: var(--border-radius-md);
|
||||
border-bottom-right-radius: var(--border-radius-md);*/
|
||||
}
|
||||
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.2rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@@ -62,12 +78,8 @@
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.participants,
|
||||
.createdBy {
|
||||
font-size: 0.875rem;
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
.participants {
|
||||
margin: 0;
|
||||
font-size: 1rem;
|
||||
color: #6b7280;
|
||||
@@ -117,8 +129,8 @@
|
||||
}
|
||||
|
||||
.expandedContent {
|
||||
margin-top: 1.5rem;
|
||||
padding-top: 1.5rem;
|
||||
padding-top:1rem;
|
||||
margin-top:1rem;
|
||||
border-top: 1px solid #E5E5E5;
|
||||
}
|
||||
|
||||
@@ -156,7 +168,17 @@
|
||||
font-size: 0.8rem;
|
||||
color: #717171;
|
||||
font-weight: 500;
|
||||
margin-bottom: 0.5rem;
|
||||
/*margin-bottom: 0.5rem;*/
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.compactElementHeading {
|
||||
font-size: 0.75rem;
|
||||
color: var(--text-tertiary);
|
||||
font-weight: 500;
|
||||
margin-bottom: 0.4rem;
|
||||
margin-top: 0;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React from 'react';
|
||||
import { useBookingContext } from '../../context/BookingContext';
|
||||
import { useSettingsContext } from '../../context/SettingsContext';
|
||||
import { Label } from '../ui/Label';
|
||||
import styles from './BookingTitleField.module.css';
|
||||
|
||||
export function BookingTitleField({ compact = false, hideLabel = false }) {
|
||||
@@ -10,7 +11,7 @@ export function BookingTitleField({ compact = false, hideLabel = false }) {
|
||||
return (
|
||||
<>
|
||||
{!hideLabel && (
|
||||
<h3 className={compact ? styles.compactElementHeading : styles.elementHeading}>Titel på bokning</h3>
|
||||
<Label>Titel på bokning</Label>
|
||||
)}
|
||||
<input
|
||||
type="text"
|
||||
|
||||
@@ -2,6 +2,7 @@ import React, { useState, useRef, useEffect } from 'react';
|
||||
import { PEOPLE, USER } from '../../constants/bookingConstants';
|
||||
import { useBookingContext } from '../../context/BookingContext';
|
||||
import { useSettingsContext } from '../../context/SettingsContext';
|
||||
import { Label } from '../ui/Label';
|
||||
import styles from './ParticipantsSelector.module.css';
|
||||
|
||||
export function ParticipantsSelector({ compact = false, hideLabel = false }) {
|
||||
@@ -170,7 +171,7 @@ export function ParticipantsSelector({ compact = false, hideLabel = false }) {
|
||||
return (
|
||||
<div className={compact ? styles.compactContainer : styles.container}>
|
||||
{!hideLabel && (
|
||||
<h3 className={compact ? styles.compactElementHeading : styles.elementHeading}>Deltagare</h3>
|
||||
<Label>Deltagare</Label>
|
||||
)}
|
||||
|
||||
{/* Search Input */}
|
||||
|
||||
@@ -3,4 +3,10 @@
|
||||
margin: 0 auto;
|
||||
padding: var(--spacing-2xl);
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.pageContainer {
|
||||
padding: var(--spacing-lg);
|
||||
}
|
||||
}
|
||||
10
my-app/src/components/ui/Label.jsx
Normal file
10
my-app/src/components/ui/Label.jsx
Normal file
@@ -0,0 +1,10 @@
|
||||
import React from 'react';
|
||||
import styles from './Label.module.css';
|
||||
|
||||
export function Label({ children, className = '', ...props }) {
|
||||
return (
|
||||
<h3 className={`${styles.label} ${className}`} {...props}>
|
||||
{children}
|
||||
</h3>
|
||||
);
|
||||
}
|
||||
9
my-app/src/components/ui/Label.module.css
Normal file
9
my-app/src/components/ui/Label.module.css
Normal file
@@ -0,0 +1,9 @@
|
||||
.label {
|
||||
font-size: 0.75rem;
|
||||
color: var(--text-tertiary);
|
||||
font-weight: 500;
|
||||
margin-bottom: 0.4rem;
|
||||
margin-top: 0;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
Reference in New Issue
Block a user