improving-week-36 #1
146
my-app/src/components/InlineBookingForm.jsx
Normal file
146
my-app/src/components/InlineBookingForm.jsx
Normal file
@@ -0,0 +1,146 @@
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import { Button } from 'react-aria-components';
|
||||
import { convertDateObjectToString, getTimeFromIndex } from '../helpers';
|
||||
import Dropdown from './Dropdown';
|
||||
import { useBookingContext } from '../context/BookingContext';
|
||||
import { useSettingsContext } from '../context/SettingsContext';
|
||||
import styles from './InlineBookingForm.module.css';
|
||||
|
||||
export function InlineBookingForm({
|
||||
startTimeIndex,
|
||||
hoursAvailable,
|
||||
onClose,
|
||||
arrowPointsLeft = true
|
||||
}) {
|
||||
const booking = useBookingContext();
|
||||
const { getCurrentUser } = useSettingsContext();
|
||||
|
||||
// Initialize with pre-selected booking length if available, or auto-select if only 30 min available
|
||||
const initialLength = booking.selectedBookingLength > 0 ? booking.selectedBookingLength :
|
||||
(hoursAvailable === 1 ? 1 : null); // Auto-select 30 min if that's all that's available
|
||||
const [selectedLength, setSelectedLength] = useState(null);
|
||||
const [calculatedEndTime, setCalculatedEndTime] = useState(startTimeIndex);
|
||||
const hasInitialized = useRef(false);
|
||||
|
||||
// Effect to handle initial setup only once when form opens
|
||||
useEffect(() => {
|
||||
if (initialLength && !hasInitialized.current) {
|
||||
setSelectedLength(initialLength);
|
||||
const newEndTime = startTimeIndex + initialLength;
|
||||
setCalculatedEndTime(newEndTime);
|
||||
booking.setSelectedEndIndex(newEndTime);
|
||||
hasInitialized.current = true;
|
||||
}
|
||||
}, [initialLength, startTimeIndex, booking]);
|
||||
|
||||
const bookingLengths = [
|
||||
{ value: 1, label: "30 min" },
|
||||
{ value: 2, label: "1 h" },
|
||||
{ value: 3, label: "1.5 h" },
|
||||
{ value: 4, label: "2 h" },
|
||||
{ value: 5, label: "2.5 h" },
|
||||
{ value: 6, label: "3 h" },
|
||||
{ value: 7, label: "3.5 h" },
|
||||
{ value: 8, label: "4 h" },
|
||||
];
|
||||
|
||||
const disabledOptions = {
|
||||
1: !(hoursAvailable > 0),
|
||||
2: !(hoursAvailable > 1),
|
||||
3: !(hoursAvailable > 2),
|
||||
4: !(hoursAvailable > 3),
|
||||
5: !(hoursAvailable > 4),
|
||||
6: !(hoursAvailable > 5),
|
||||
7: !(hoursAvailable > 6),
|
||||
8: !(hoursAvailable > 7),
|
||||
};
|
||||
|
||||
function handleChange(event) {
|
||||
const lengthValue = event.target.value === "" ? null : parseInt(event.target.value);
|
||||
setSelectedLength(lengthValue);
|
||||
|
||||
if (lengthValue !== null) {
|
||||
const newEndTime = startTimeIndex + lengthValue;
|
||||
setCalculatedEndTime(newEndTime);
|
||||
booking.setSelectedEndIndex(newEndTime);
|
||||
} else {
|
||||
// Reset to default state when placeholder is selected
|
||||
setCalculatedEndTime(startTimeIndex);
|
||||
booking.setSelectedEndIndex(null);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if user has selected a booking length (including pre-selected)
|
||||
const hasSelectedLength = selectedLength !== null;
|
||||
|
||||
// Display time range - show calculated end time if length is selected
|
||||
const displayEndTime = hasSelectedLength ? calculatedEndTime : startTimeIndex;
|
||||
|
||||
return (
|
||||
<div className={`${styles.inlineForm} ${arrowPointsLeft ? styles.arrowLeft : styles.arrowRight}`}>
|
||||
<div className={styles.formHeader}>
|
||||
<h3>{booking.title == "" ? "Jacobs bokning" : booking.title}</h3>
|
||||
<p className={styles.dateText}>{convertDateObjectToString(booking.selectedDate)}</p>
|
||||
</div>
|
||||
|
||||
<div className={styles.timeDisplay}>
|
||||
<div className={styles.timeRange}>
|
||||
<div className={styles.timeItem}>
|
||||
<label>Starttid</label>
|
||||
<span className={styles.timeValue}>{getTimeFromIndex(startTimeIndex)}</span>
|
||||
</div>
|
||||
<div className={styles.timeSeparator}>–</div>
|
||||
<div className={styles.timeItem}>
|
||||
<label>Sluttid</label>
|
||||
<span className={`${styles.timeValue} ${!hasSelectedLength ? styles.placeholder : ''}`}>
|
||||
{hasSelectedLength ? getTimeFromIndex(displayEndTime) : "Välj längd"}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.formField}>
|
||||
<label>Längd</label>
|
||||
<Dropdown
|
||||
options={bookingLengths}
|
||||
disabledOptions={disabledOptions}
|
||||
onChange={handleChange}
|
||||
value={selectedLength || ""}
|
||||
placeholder={!initialLength ? {
|
||||
value: "",
|
||||
label: "Välj bokningslängd"
|
||||
} : null}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className={styles.sectionWithTitle}>
|
||||
<label>{booking.selectedRoom !== "allRooms" ? "Rum" : "Tilldelat rum"}</label>
|
||||
<p>{booking.selectedRoom !== "allRooms" ? booking.selectedRoom : (booking.assignedRoom || 'Inget rum tilldelat')}</p>
|
||||
</div>
|
||||
|
||||
<div className={styles.sectionWithTitle}>
|
||||
<label>Deltagare</label>
|
||||
<p>
|
||||
{(() => {
|
||||
const currentUser = getCurrentUser();
|
||||
const allParticipants = [currentUser, ...booking.participants.filter(p => p.id !== currentUser.id)];
|
||||
return allParticipants.map(p => p.name).join(", ");
|
||||
})()}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className={styles.formActions}>
|
||||
<Button className={styles.cancelButton} onPress={onClose}>
|
||||
Avbryt
|
||||
</Button>
|
||||
<Button
|
||||
className={`${styles.saveButton} ${!hasSelectedLength ? styles.disabledButton : ''}`}
|
||||
onPress={hasSelectedLength ? booking.handleSave : undefined}
|
||||
isDisabled={!hasSelectedLength}
|
||||
>
|
||||
{hasSelectedLength ? 'Boka' : 'Välj längd först'}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
243
my-app/src/components/InlineBookingForm.module.css
Normal file
243
my-app/src/components/InlineBookingForm.module.css
Normal file
@@ -0,0 +1,243 @@
|
||||
.inlineForm {
|
||||
background: white;
|
||||
border: 1px solid #D1D5DB;
|
||||
border-radius: 0.5rem;
|
||||
padding: 1.5rem;
|
||||
margin: 1rem 0;
|
||||
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
|
||||
animation: slideDown 0.2s ease-out;
|
||||
width: 100%;
|
||||
flex-basis: 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* Arrow pointing to left card */
|
||||
.arrowLeft::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -8px;
|
||||
left: 75px;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 8px solid transparent;
|
||||
border-right: 8px solid transparent;
|
||||
border-bottom: 8px solid #D1D5DB;
|
||||
}
|
||||
|
||||
.arrowLeft::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -7px;
|
||||
left: 76px;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 7px solid transparent;
|
||||
border-right: 7px solid transparent;
|
||||
border-bottom: 7px solid white;
|
||||
}
|
||||
|
||||
/* Arrow pointing to right card */
|
||||
.arrowRight::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -8px;
|
||||
right: 75px;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 8px solid transparent;
|
||||
border-right: 8px solid transparent;
|
||||
border-bottom: 8px solid #D1D5DB;
|
||||
}
|
||||
|
||||
.arrowRight::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -7px;
|
||||
right: 76px;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 7px solid transparent;
|
||||
border-right: 7px solid transparent;
|
||||
border-bottom: 7px solid white;
|
||||
}
|
||||
|
||||
.formHeader {
|
||||
text-align: center;
|
||||
margin-bottom: 1.5rem;
|
||||
padding-bottom: 1rem;
|
||||
border-bottom: 1px solid #E5E7EB;
|
||||
}
|
||||
|
||||
.formHeader h3 {
|
||||
margin: 0 0 0.5rem 0;
|
||||
font-size: 1.25rem;
|
||||
font-weight: 700;
|
||||
color: #111827;
|
||||
}
|
||||
|
||||
.dateText {
|
||||
margin: 0;
|
||||
color: #6B7280;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.timeDisplay {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.timeRange {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 1rem;
|
||||
padding: 1rem;
|
||||
background: #F9FAFB;
|
||||
border-radius: 0.375rem;
|
||||
}
|
||||
|
||||
.timeItem {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.timeItem label {
|
||||
font-size: 0.75rem;
|
||||
color: #6B7280;
|
||||
font-weight: 500;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
}
|
||||
|
||||
.timeValue {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 600;
|
||||
color: #111827;
|
||||
}
|
||||
|
||||
.timeValue.placeholder {
|
||||
color: #9CA3AF;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.timeSeparator {
|
||||
font-size: 1.5rem;
|
||||
color: #6B7280;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.formField {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.formField label {
|
||||
display: block;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
color: #374151;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.sectionWithTitle {
|
||||
padding-top: 1rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
.sectionWithTitle label {
|
||||
font-size: 0.8rem;
|
||||
color: #717171;
|
||||
}
|
||||
|
||||
.sectionWithTitle p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.formActions {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
margin-top: 1.5rem;
|
||||
padding-top: 1rem;
|
||||
border-top: 1px solid #E5E7EB;
|
||||
}
|
||||
|
||||
.cancelButton {
|
||||
flex: 1;
|
||||
background-color: white;
|
||||
height: 2.75rem;
|
||||
color: #374151;
|
||||
font-weight: 600;
|
||||
border: 2px solid #d1d5db;
|
||||
border-radius: 0.375rem;
|
||||
transition: all 0.2s ease;
|
||||
cursor: pointer;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.cancelButton:hover {
|
||||
background-color: #f9fafb;
|
||||
border-color: #9ca3af;
|
||||
}
|
||||
|
||||
.cancelButton:active {
|
||||
background-color: #e5e7eb;
|
||||
transform: translateY(1px);
|
||||
}
|
||||
|
||||
.saveButton {
|
||||
flex: 2;
|
||||
background-color: #059669;
|
||||
color: white;
|
||||
height: 2.75rem;
|
||||
font-weight: 600;
|
||||
font-size: 0.875rem;
|
||||
border: 2px solid #047857;
|
||||
border-radius: 0.375rem;
|
||||
transition: all 0.2s ease;
|
||||
box-shadow: 0 2px 4px rgba(5, 150, 105, 0.2);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.saveButton:hover {
|
||||
background-color: #047857;
|
||||
box-shadow: 0 4px 8px rgba(5, 150, 105, 0.3);
|
||||
}
|
||||
|
||||
.saveButton:active {
|
||||
background-color: #065f46;
|
||||
transform: translateY(1px);
|
||||
box-shadow: 0 1px 2px rgba(5, 150, 105, 0.2);
|
||||
}
|
||||
|
||||
.disabledButton {
|
||||
background-color: #f8f9fa !important;
|
||||
color: #adb5bd !important;
|
||||
border: 2px dashed #dee2e6 !important;
|
||||
opacity: 0.6 !important;
|
||||
box-shadow: none !important;
|
||||
cursor: default !important;
|
||||
}
|
||||
|
||||
.disabledButton:hover {
|
||||
background-color: #f8f9fa !important;
|
||||
transform: none !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
.disabledButton:active {
|
||||
background-color: #f8f9fa !important;
|
||||
transform: none !important;
|
||||
}
|
||||
|
||||
@keyframes slideDown {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,7 @@
|
||||
import { Button, DialogTrigger } from 'react-aria-components';
|
||||
import React, { useState } from 'react';
|
||||
import { Button } from 'react-aria-components';
|
||||
import React from 'react';
|
||||
import styles from './TimeCard.module.css';
|
||||
import { useBookingContext } from '../context/BookingContext';
|
||||
import { BookingModal } from './BookingModal';
|
||||
|
||||
export default function TimeCard({
|
||||
startTimeIndex,
|
||||
@@ -20,8 +19,6 @@ export default function TimeCard({
|
||||
// Use the pre-selected booking length if available, otherwise use available hours
|
||||
const displayHours = booking.selectedBookingLength > 0 ? booking.selectedBookingLength : hoursAvailable;
|
||||
const halfHours = displayHours;
|
||||
|
||||
const [endTimeIndex, setEndTimeIndex] = useState(startTimeIndex + hoursAvailable);
|
||||
|
||||
if (halfHours === 1) {
|
||||
hoursText = "30\u202Fmin";
|
||||
@@ -41,7 +38,7 @@ export default function TimeCard({
|
||||
return `${hour}:${minute}`;
|
||||
}
|
||||
|
||||
let classNames = selected ? `${styles.container}` : styles.container;
|
||||
let classNames = selected ? `${styles.container} ${styles.selected}` : styles.container;
|
||||
|
||||
const className = state === "unavailableSlot" ? styles.unavailableSlot : styles.availableSlot;
|
||||
|
||||
@@ -52,37 +49,24 @@ export default function TimeCard({
|
||||
|
||||
if (state === "availableSlot") {
|
||||
return (
|
||||
<DialogTrigger>
|
||||
<Button
|
||||
className={`${classNames} ${className}`}
|
||||
onClick={() => {
|
||||
if (state === "availableSlot") {
|
||||
handleClick();
|
||||
}
|
||||
console.log("state: ", state);
|
||||
}}
|
||||
onMouseEnter={() => handleTimeCardHover(startTimeIndex)}
|
||||
onMouseLeave={handleTimeCardExit}
|
||||
>
|
||||
{(!isEndState && hoursAvailable > 0) || isEndState || state=="availableSlot" ? (
|
||||
<>
|
||||
<p className={styles.startTime}>{formatSlotIndex(startTimeIndex)}</p>
|
||||
<p className={styles.upToText}>{hoursAvailable > 1 && booking.selectedBookingLength === 0 && "Upp till "}<span className={styles.hoursText}>{hoursText}</span></p>
|
||||
</>
|
||||
) : null}
|
||||
</Button>
|
||||
<BookingModal
|
||||
startTimeIndex={startTimeIndex}
|
||||
hoursAvailable={hoursAvailable}
|
||||
endTimeIndex={endTimeIndex}
|
||||
setEndTimeIndex={setEndTimeIndex}
|
||||
className={styles.modalContainer}
|
||||
onClose={() => {
|
||||
// Reset time selections when modal is closed without booking
|
||||
booking.resetTimeSelections();
|
||||
}}
|
||||
/>
|
||||
</DialogTrigger>
|
||||
<Button
|
||||
className={`${classNames} ${className}`}
|
||||
onClick={() => {
|
||||
if (state === "availableSlot") {
|
||||
handleClick();
|
||||
}
|
||||
console.log("state: ", state);
|
||||
}}
|
||||
onMouseEnter={() => handleTimeCardHover(startTimeIndex)}
|
||||
onMouseLeave={handleTimeCardExit}
|
||||
>
|
||||
{(!isEndState && hoursAvailable > 0) || isEndState || state=="availableSlot" ? (
|
||||
<>
|
||||
<p className={styles.startTime}>{formatSlotIndex(startTimeIndex)}</p>
|
||||
<p className={styles.upToText}>{hoursAvailable > 1 && booking.selectedBookingLength === 0 && "Upp till "}<span className={styles.hoursText}>{hoursText}</span></p>
|
||||
</>
|
||||
) : null}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -35,6 +35,21 @@
|
||||
outline-offset: -1px;
|
||||
}
|
||||
|
||||
.selected {
|
||||
background-color: #2563EB !important;
|
||||
color: white !important;
|
||||
border-color: #1d4ed8 !important;
|
||||
box-shadow: 0 2px 8px rgba(37, 99, 235, 0.3);
|
||||
}
|
||||
|
||||
.selected .upToText {
|
||||
color: rgba(255, 255, 255, 0.8) !important;
|
||||
}
|
||||
|
||||
.selected .hoursText {
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
.container p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React from 'react';
|
||||
import TimeCard from './TimeCard';
|
||||
import { InlineBookingForm } from './InlineBookingForm';
|
||||
import styles from './TimeCardContainer.module.css';
|
||||
import { useBookingContext } from '../context/BookingContext';
|
||||
|
||||
@@ -42,25 +43,24 @@ export function TimeCardContainer() {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.columnContainer}>
|
||||
{slotIndiciesToColumns(slotIndices).map((column, index) => {
|
||||
|
||||
return (
|
||||
<div key={index} className={styles.column}>
|
||||
{
|
||||
|
||||
column.map(index => {
|
||||
<div>
|
||||
<div className={styles.columnContainer}>
|
||||
{slotIndiciesToColumns(slotIndices).map((column, index) => {
|
||||
|
||||
return (
|
||||
<div key={index} className={styles.column}>
|
||||
{column.map(slotIndex => {
|
||||
let maxConsecutive = 0;
|
||||
let roomId = "";
|
||||
|
||||
if (booking.currentRoom) {
|
||||
const consecutive = countConsecutiveFromSlot(booking.currentRoom.times, index);
|
||||
const consecutive = countConsecutiveFromSlot(booking.currentRoom.times, slotIndex);
|
||||
if (consecutive > maxConsecutive) {
|
||||
maxConsecutive = consecutive;
|
||||
}
|
||||
} else {
|
||||
booking.timeSlotsByRoom.forEach(room => {
|
||||
const consecutive = countConsecutiveFromSlot(room.times, index);
|
||||
const consecutive = countConsecutiveFromSlot(room.times, slotIndex);
|
||||
if (consecutive > maxConsecutive) {
|
||||
maxConsecutive = consecutive;
|
||||
roomId = room.roomId;
|
||||
@@ -79,8 +79,8 @@ export function TimeCardContainer() {
|
||||
if (booking.selectedBookingLength !== 0) {
|
||||
// Check if this slot can accommodate the selected booking length
|
||||
const actualConsecutive = booking.currentRoom ?
|
||||
countConsecutiveFromSlot(booking.currentRoom.times, index) :
|
||||
Math.max(...booking.timeSlotsByRoom.map(room => countConsecutiveFromSlot(room.times, index)));
|
||||
countConsecutiveFromSlot(booking.currentRoom.times, slotIndex) :
|
||||
Math.max(...booking.timeSlotsByRoom.map(room => countConsecutiveFromSlot(room.times, slotIndex)));
|
||||
|
||||
if (actualConsecutive >= booking.selectedBookingLength) {
|
||||
timeCardState = "availableSlot";
|
||||
@@ -91,23 +91,57 @@ export function TimeCardContainer() {
|
||||
timeCardState = "availableSlot";
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
const elements = [];
|
||||
|
||||
elements.push(
|
||||
<TimeCard
|
||||
key={index}
|
||||
startTimeIndex={index}
|
||||
key={slotIndex}
|
||||
startTimeIndex={slotIndex}
|
||||
hoursAvailable={maxConsecutive}
|
||||
handleClick={() => booking.handleTimeCardClick(index, maxConsecutive, roomId)}
|
||||
selected={index === booking.selectedStartIndex}
|
||||
handleClick={() => {
|
||||
if (booking.selectedStartIndex === slotIndex) {
|
||||
// If clicking on already selected card, close the form
|
||||
booking.resetTimeSelections();
|
||||
} else {
|
||||
// Otherwise, select this card
|
||||
booking.handleTimeCardClick(slotIndex, maxConsecutive, roomId);
|
||||
}
|
||||
}}
|
||||
selected={slotIndex === booking.selectedStartIndex}
|
||||
state={timeCardState}
|
||||
handleTimeCardHover={() => {}}
|
||||
handleTimeCardExit={booking.handleTimeCardExit}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
||||
// Add inline booking form after the pair that contains the selected time card
|
||||
// Cards are laid out in pairs: (0,1), (2,3), (4,5), etc.
|
||||
if (booking.selectedStartIndex !== null) {
|
||||
const selectedPairStart = Math.floor(booking.selectedStartIndex / 2) * 2;
|
||||
const selectedPairEnd = selectedPairStart + 1;
|
||||
|
||||
// Show form after the second card of the pair that contains the selected card
|
||||
if (slotIndex === selectedPairEnd && (booking.selectedStartIndex === selectedPairStart || booking.selectedStartIndex === selectedPairEnd)) {
|
||||
const isLeftCard = booking.selectedStartIndex === selectedPairStart;
|
||||
elements.push(
|
||||
<InlineBookingForm
|
||||
key={`form-${slotIndex}`}
|
||||
startTimeIndex={booking.selectedStartIndex}
|
||||
hoursAvailable={booking.selectedEndIndex - booking.selectedStartIndex}
|
||||
onClose={() => booking.resetTimeSelections()}
|
||||
arrowPointsLeft={isLeftCard}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return elements;
|
||||
}).flat()}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -13,7 +13,7 @@
|
||||
width: 350px;
|
||||
gap: 0.5rem;
|
||||
height: fit-content;
|
||||
align-items: center;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user