booking-flow-finalized-design kindaaaa #7
@@ -33,6 +33,7 @@ const AppRoutes = () => {
|
||||
roomCategory: 'green',
|
||||
title: 'Team standup',
|
||||
participants: [
|
||||
{ id: 1, name: 'Jacob Reinikainen', username: 'jare2473', email: 'jacob.reinikainen@dsv.su.se' },
|
||||
{ id: 2, name: 'Filip Norgren', username: 'fino2341', email: 'filip.norgren@dsv.su.se' },
|
||||
{ id: 3, name: 'Hedvig Engelmark', username: 'heen9876', email: 'hedvig.engelmark@dsv.su.se' },
|
||||
{ id: 4, name: 'Elin Rudling', username: 'elru4521', email: 'elin.rudling@dsv.su.se' }
|
||||
@@ -47,6 +48,7 @@ const AppRoutes = () => {
|
||||
roomCategory: 'red',
|
||||
title: 'Project planning workshop',
|
||||
participants: [
|
||||
{ id: 1, name: 'Jacob Reinikainen', username: 'jare2473', email: 'jacob.reinikainen@dsv.su.se' },
|
||||
{ id: 5, name: 'Victor Magnusson', username: 'vima8734', email: 'victor.magnusson@dsv.su.se' },
|
||||
{ id: 6, name: 'Ellen Britschgi', username: 'elbr5623', email: 'ellen.britschgi@dsv.su.se' },
|
||||
{ id: 7, name: 'Anna Andersson', username: 'anan3457', email: 'anna.andersson@dsv.su.se' },
|
||||
@@ -64,6 +66,7 @@ const AppRoutes = () => {
|
||||
roomCategory: 'blue',
|
||||
title: '1:1 with supervisor',
|
||||
participants: [
|
||||
{ id: 1, name: 'Jacob Reinikainen', username: 'jare2473', email: 'jacob.reinikainen@dsv.su.se' },
|
||||
{ id: 251, name: 'Arjohn Emilsson', username: 'arem1532', email: 'arjohn.emilsson@dsv.su.se' }
|
||||
]
|
||||
},
|
||||
@@ -76,9 +79,27 @@ const AppRoutes = () => {
|
||||
roomCategory: 'yellow',
|
||||
title: 'Study group session',
|
||||
participants: [
|
||||
{ id: 1, name: 'Jacob Reinikainen', username: 'jare2473', email: 'jacob.reinikainen@dsv.su.se' },
|
||||
{ id: 11, name: 'Emma Johansson', username: 'emjo4512', email: 'emma.johansson@dsv.su.se' },
|
||||
{ id: 12, name: 'Oskar Pettersson', username: 'ospe3698', email: 'oskar.pettersson@dsv.su.se' }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
date: new CalendarDate(2025, 9, 7),
|
||||
startTime: 10,
|
||||
endTime: 12,
|
||||
room: 'G5:9',
|
||||
roomCategory: 'blue',
|
||||
title: 'Design review meeting',
|
||||
createdBy: { id: 3, name: 'Hedvig Engelmark', username: 'heen9876', email: 'hedvig.engelmark@dsv.su.se' },
|
||||
participants: [
|
||||
{ id: 3, name: 'Hedvig Engelmark', username: 'heen9876', email: 'hedvig.engelmark@dsv.su.se' },
|
||||
{ id: 1, name: 'Jacob Reinikainen', username: 'jare2473', email: 'jacob.reinikainen@dsv.su.se' },
|
||||
{ id: 5, name: 'Victor Magnusson', username: 'vima8734', email: 'victor.magnusson@dsv.su.se' },
|
||||
{ id: 8, name: 'Erik Larsson', username: 'erla7892', email: 'erik.larsson@dsv.su.se' }
|
||||
],
|
||||
isParticipantBooking: true
|
||||
}
|
||||
]);
|
||||
|
||||
|
||||
@@ -6,9 +6,11 @@ import Dropdown from '../ui/Dropdown';
|
||||
import { BookingTitleField } from '../forms/BookingTitleField';
|
||||
import { ParticipantsSelector } from '../forms/ParticipantsSelector';
|
||||
import { BookingProvider } from '../../context/BookingContext';
|
||||
import { PEOPLE } from '../../constants/bookingConstants';
|
||||
import { PEOPLE, USER } from '../../constants/bookingConstants';
|
||||
|
||||
function BookingCard({ booking, onClick, isExpanded, onBookingUpdate, onBookingDelete }) {
|
||||
// Check if this is a participant booking (user was added by someone else)
|
||||
const isParticipantBooking = booking.isParticipantBooking === true;
|
||||
const [selectedLength, setSelectedLength] = useState(null);
|
||||
const [calculatedEndTime, setCalculatedEndTime] = useState(null);
|
||||
const [editedTitle, setEditedTitle] = useState('');
|
||||
@@ -118,6 +120,14 @@ function BookingCard({ booking, onClick, isExpanded, onBookingUpdate, onBookingD
|
||||
setShowDeleteConfirm(false);
|
||||
}
|
||||
|
||||
function handleRemoveSelf() {
|
||||
// For participant bookings, remove the current user from participants
|
||||
// This effectively "leaves" the booking
|
||||
if (isParticipantBooking && onBookingDelete) {
|
||||
onBookingDelete(booking);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function getTimeFromIndex(timeIndex) {
|
||||
const totalHalfHoursFromStart = timeIndex;
|
||||
@@ -133,13 +143,35 @@ function BookingCard({ booking, onClick, isExpanded, onBookingUpdate, onBookingD
|
||||
function formatParticipants(participants) {
|
||||
if (!participants || participants.length === 0) return null;
|
||||
|
||||
const formatName = (participant, index) => {
|
||||
// Bold the booker's name (creator in participant bookings, or current user in regular bookings)
|
||||
const isBooker = isParticipantBooking
|
||||
? (booking.createdBy && participant.id === booking.createdBy.id)
|
||||
: (participant.id === USER.id);
|
||||
|
||||
const firstName = participant.name.split(' ')[0];
|
||||
|
||||
if (isBooker) {
|
||||
return <strong key={`participant-${participant.id}-${index}`}>{firstName}</strong>;
|
||||
}
|
||||
return <span key={`participant-${participant.id}-${index}`}>{firstName}</span>;
|
||||
};
|
||||
|
||||
if (participants.length === 1) {
|
||||
return participants[0].name;
|
||||
return formatName(participants[0], 0);
|
||||
} else if (participants.length === 2) {
|
||||
return `${participants[0].name} and ${participants[1].name}`;
|
||||
return (
|
||||
<>
|
||||
{formatName(participants[0], 0)} and {formatName(participants[1], 1)}
|
||||
</>
|
||||
);
|
||||
} else {
|
||||
const remaining = participants.length - 2;
|
||||
return `${participants[0].name}, ${participants[1].name} and ${remaining} more`;
|
||||
return (
|
||||
<>
|
||||
{formatName(participants[0], 0)}, {formatName(participants[1], 1)} and {remaining} more
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,95 +179,166 @@ function BookingCard({ booking, onClick, isExpanded, onBookingUpdate, onBookingD
|
||||
<div className={`${styles.card} ${isExpanded ? styles.expanded : ''}`}>
|
||||
<div className={styles.header} onClick={!isExpanded ? onClick : undefined}>
|
||||
<div className={styles.leftSection}>
|
||||
<span className={styles.date}>{convertDateObjectToString(booking.date)}</span>
|
||||
<div className={styles.titleRow}>
|
||||
<h3 className={styles.title}>{booking.title}</h3>
|
||||
</div>
|
||||
<div className={styles.roomAndParticipants}>
|
||||
<span className={styles.room}>{booking.room}</span>
|
||||
{booking.participants && booking.participants.length > 0 && (
|
||||
<p className={styles.participants}>{formatParticipants(booking.participants)}</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
|
||||
{isParticipantBooking && booking.createdBy && (
|
||||
<div className={styles.createdBy}>
|
||||
Tillagd av {booking.createdBy.name}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!isParticipantBooking && booking.participants && booking.participants.length > 0 && (
|
||||
<div className={styles.participants}>{formatParticipants(booking.participants)}</div>
|
||||
)}
|
||||
</div>
|
||||
<div className={styles.timeSection}>
|
||||
<div className={styles.startTime}>{getTimeFromIndex(booking.startTime)}</div>
|
||||
<div className={styles.endTime}>{getTimeFromIndex(calculatedEndTime || booking.endTime)}</div>
|
||||
<div className={styles.rightSection}>
|
||||
<div className={styles.time}>
|
||||
{getTimeFromIndex(booking.startTime)} – {getTimeFromIndex(calculatedEndTime || booking.endTime)}
|
||||
</div>
|
||||
<div className={styles.roomBadge}>
|
||||
{booking.room}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{isExpanded && (
|
||||
<BookingProvider value={localBookingContext}>
|
||||
<div className={styles.expandedContent}>
|
||||
<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>
|
||||
{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}
|
||||
</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>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<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>
|
||||
// Regular booking view - editable
|
||||
<>
|
||||
<div className={styles.formSection}>
|
||||
<BookingTitleField compact={true} />
|
||||
</div>
|
||||
<div className={styles.confirmationButtons}>
|
||||
<Button
|
||||
className={styles.confirmDeleteButton}
|
||||
onPress={confirmDelete}
|
||||
>
|
||||
Ja, radera
|
||||
</Button>
|
||||
<Button
|
||||
className={styles.cancelDeleteButton}
|
||||
onPress={cancelDelete}
|
||||
>
|
||||
Avbryt
|
||||
</Button>
|
||||
|
||||
<div className={styles.formSection}>
|
||||
<ParticipantsSelector compact={true} />
|
||||
</div>
|
||||
</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}
|
||||
>
|
||||
Ja, radera
|
||||
</Button>
|
||||
<Button
|
||||
className={styles.cancelDeleteButton}
|
||||
onPress={cancelDelete}
|
||||
>
|
||||
Avbryt
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</BookingProvider>
|
||||
|
||||
@@ -1,56 +1,83 @@
|
||||
.card {
|
||||
border: var(--border-width-thin) solid var(--border-light);
|
||||
padding: var(--card-padding);
|
||||
padding: 1rem;
|
||||
width: 100%;
|
||||
border-radius: var(--border-radius-md);
|
||||
background: var(--bg-primary);
|
||||
transition: var(--transition-medium);
|
||||
box-shadow: var(--shadow-lg);
|
||||
/*box-shadow: var(--shadow-lg);*/
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
cursor: pointer;
|
||||
border-color: var(--color-primary);
|
||||
box-shadow: var(--shadow-xl);
|
||||
/*transform: translateY(-2px);*/
|
||||
@media (hover: hover) {
|
||||
.card:hover {
|
||||
cursor: pointer;
|
||||
border-color: var(--color-primary);
|
||||
box-shadow: var(--shadow-xl);
|
||||
/*transform: translateY(-2px);*/
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
align-items: flex-start;
|
||||
gap: 1rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.leftSection {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.date {
|
||||
text-transform: uppercase;
|
||||
font-size: var(--font-size-sm);
|
||||
font-weight: var(--font-weight-semibold);
|
||||
color: var(--text-muted);
|
||||
letter-spacing: 1px;
|
||||
margin-bottom: var(--spacing-sm);
|
||||
display: block;
|
||||
}
|
||||
|
||||
.titleRow {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-md);
|
||||
margin-bottom: var(--spacing-sm);
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.rightSection {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
align-items: end
|
||||
}
|
||||
|
||||
.createdByIndicator {
|
||||
font-size: var(--font-size-sm);
|
||||
color: var(--text-muted);
|
||||
font-weight: var(--font-weight-normal);
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin: 0;
|
||||
font-size: var(--font-size-3xl);
|
||||
font-weight: var(--font-weight-bold);
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.time {
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
color: #111827;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.createdBy {
|
||||
font-size: 0.875rem;
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
.roomBadge {
|
||||
background-color: #065f46;
|
||||
color: white;
|
||||
padding: 0.25rem 0.75rem;
|
||||
border-radius: 10rem;
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.room {
|
||||
font-weight: var(--font-weight-semibold);
|
||||
font-size: var(--font-size-xl);
|
||||
@@ -89,8 +116,8 @@
|
||||
|
||||
.participants {
|
||||
margin: 0;
|
||||
font-size: var(--font-size-md);
|
||||
color: var(--text-muted);
|
||||
font-size: 1rem;
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
/* Expanded card styles */
|
||||
@@ -117,6 +144,27 @@
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.readOnlySection {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.readOnlyField {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.readOnlyField:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.createdByText,
|
||||
.participantsText,
|
||||
.timeText {
|
||||
margin: 0;
|
||||
font-size: var(--font-size-base);
|
||||
color: var(--text-primary);
|
||||
font-weight: var(--font-weight-medium);
|
||||
}
|
||||
|
||||
.editSection {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
@@ -15,10 +15,31 @@ function BookingsList({ bookings, handleEditBooking, onBookingUpdate, onBookingD
|
||||
return dateA - dateB;
|
||||
});
|
||||
|
||||
// Group bookings by date
|
||||
const groupedBookings = sortedBookings.reduce((groups, booking) => {
|
||||
const dateKey = `${booking.date.year}-${booking.date.month}-${booking.date.day}`;
|
||||
if (!groups[dateKey]) {
|
||||
groups[dateKey] = [];
|
||||
}
|
||||
groups[dateKey].push(booking);
|
||||
return groups;
|
||||
}, {});
|
||||
|
||||
function handleBookingClick(booking) {
|
||||
setExpandedBookingId(expandedBookingId === booking.id ? null : booking.id);
|
||||
}
|
||||
|
||||
function formatDateHeader(dateObj) {
|
||||
const days = ['SÖNDAG', 'MÅNDAG', 'TISDAG', 'ONSDAG', 'TORSDAG', 'FREDAG', 'LÖRDAG'];
|
||||
const months = ['JANUARI', 'FEBRUARI', 'MARS', 'APRIL', 'MAJ', 'JUNI', 'JULI', 'AUGUSTI', 'SEPTEMBER', 'OKTOBER', 'NOVEMBER', 'DECEMBER'];
|
||||
|
||||
const date = new Date(dateObj.year, dateObj.month - 1, dateObj.day);
|
||||
const dayName = days[date.getDay()];
|
||||
const monthName = months[dateObj.month - 1];
|
||||
|
||||
return `${dayName} ${dateObj.day} ${monthName} ${dateObj.year}`;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.bookingsListContainer}>
|
||||
{showSuccessBanner && (
|
||||
@@ -71,17 +92,24 @@ function BookingsList({ bookings, handleEditBooking, onBookingUpdate, onBookingD
|
||||
/>
|
||||
)}
|
||||
<div className={styles.bookingsContainer}>
|
||||
{sortedBookings.length > 0 ? (
|
||||
{Object.keys(groupedBookings).length > 0 ? (
|
||||
<>
|
||||
{sortedBookings.map((booking, index) => (
|
||||
<BookingCard
|
||||
key={index}
|
||||
booking={booking}
|
||||
onClick={() => handleBookingClick(booking)}
|
||||
isExpanded={expandedBookingId === booking.id}
|
||||
onBookingUpdate={onBookingUpdate}
|
||||
onBookingDelete={onBookingDelete}
|
||||
/>
|
||||
{Object.entries(groupedBookings).map(([dateKey, dayBookings]) => (
|
||||
<div key={dateKey} className={styles.dateGroup}>
|
||||
<h2 className={styles.dateHeader}>
|
||||
{formatDateHeader(dayBookings[0].date)}
|
||||
</h2>
|
||||
{dayBookings.map((booking, index) => (
|
||||
<BookingCard
|
||||
key={booking.id}
|
||||
booking={booking}
|
||||
onClick={() => handleBookingClick(booking)}
|
||||
isExpanded={expandedBookingId === booking.id}
|
||||
onBookingUpdate={onBookingUpdate}
|
||||
onBookingDelete={onBookingDelete}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
) : (
|
||||
|
||||
@@ -63,4 +63,22 @@
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.dateGroup {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.dateGroup:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.dateHeader {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
color: #9ca3af;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
margin: 0 0 0.25rem 0;
|
||||
padding: 0;
|
||||
}
|
||||
Reference in New Issue
Block a user