added profile page
This commit is contained in:
@@ -16,6 +16,7 @@ import SupportPage from "./components/SupportPage";
|
||||
import BookingManagementPage from "./components/BookingManagementPage";
|
||||
import RecurringBlockPage from "./components/RecurringBlockPage";
|
||||
import NotificationsPage from "./components/NotificationsPage";
|
||||
import ProfilePage from "./components/ProfilePage";
|
||||
|
||||
export default function App() {
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
@@ -57,7 +58,10 @@ export default function App() {
|
||||
{/* Main Content with left margin for sidebar */}
|
||||
<div className="ml-[280px]">
|
||||
{/* Header with notifications and profile */}
|
||||
<Header onNavigateToNotifications={() => setActiveNavItem("notifications")} />
|
||||
<Header
|
||||
onNavigateToNotifications={() => setActiveNavItem("notifications")}
|
||||
onNavigateToProfile={() => setActiveNavItem("profile")}
|
||||
/>
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
@@ -78,6 +82,7 @@ export default function App() {
|
||||
{activeNavItem === "staff" && <StaffManagementPage />}
|
||||
{activeNavItem === "support" && <SupportPage />}
|
||||
{activeNavItem === "notifications" && <NotificationsPage />}
|
||||
{activeNavItem === "profile" && <ProfilePage />}
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Bell, User, Settings, LogOut, ChevronDown } from "lucide-react";
|
||||
import { Bell, User, LogOut, ChevronDown } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
import { motion, AnimatePresence } from "motion/react";
|
||||
import { Avatar, AvatarFallback, AvatarImage } from "./ui/avatar";
|
||||
@@ -7,9 +7,10 @@ import { Badge } from "./ui/badge";
|
||||
|
||||
interface HeaderProps {
|
||||
onNavigateToNotifications?: () => void;
|
||||
onNavigateToProfile?: () => void;
|
||||
}
|
||||
|
||||
export default function Header({ onNavigateToNotifications }: HeaderProps) {
|
||||
export default function Header({ onNavigateToNotifications, onNavigateToProfile }: HeaderProps) {
|
||||
const [showNotifications, setShowNotifications] = useState(false);
|
||||
const [showProfileMenu, setShowProfileMenu] = useState(false);
|
||||
|
||||
@@ -166,17 +167,14 @@ export default function Header({ onNavigateToNotifications }: HeaderProps) {
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="w-full justify-start gap-2 px-3 py-2 hover:bg-gray-100"
|
||||
onClick={() => {
|
||||
setShowProfileMenu(false);
|
||||
onNavigateToProfile?.();
|
||||
}}
|
||||
>
|
||||
<User className="h-4 w-4" />
|
||||
<span>Profile</span>
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="w-full justify-start gap-2 px-3 py-2 hover:bg-gray-100"
|
||||
>
|
||||
<Settings className="h-4 w-4" />
|
||||
<span>Settings</span>
|
||||
</Button>
|
||||
<div className="border-t border-gray-100 my-1"></div>
|
||||
<Button
|
||||
variant="ghost"
|
||||
|
||||
415
src/components/ProfilePage.tsx
Normal file
415
src/components/ProfilePage.tsx
Normal file
@@ -0,0 +1,415 @@
|
||||
import { useState } from "react";
|
||||
import { motion } from "motion/react";
|
||||
import { User, Mail, Phone, MapPin, Shield, Bell, Eye, EyeOff, Camera, Save, Edit3 } from "lucide-react";
|
||||
import { Button } from "./ui/button";
|
||||
import { Input } from "./ui/input";
|
||||
import { Label } from "./ui/label";
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "./ui/card";
|
||||
import { Avatar, AvatarFallback, AvatarImage } from "./ui/avatar";
|
||||
import { Switch } from "./ui/switch";
|
||||
import { Separator } from "./ui/separator";
|
||||
import { Badge } from "./ui/badge";
|
||||
|
||||
export default function ProfilePage() {
|
||||
const [isEditing, setIsEditing] = useState(false);
|
||||
const [showCurrentPassword, setShowCurrentPassword] = useState(false);
|
||||
const [showNewPassword, setShowNewPassword] = useState(false);
|
||||
const [showConfirmPassword, setShowConfirmPassword] = useState(false);
|
||||
const [activeTab, setActiveTab] = useState("personal");
|
||||
|
||||
// Profile data states
|
||||
const [profileData, setProfileData] = useState({
|
||||
firstName: "Kassandra",
|
||||
lastName: "Adams",
|
||||
email: "kassandra.adams@citycards.com",
|
||||
phone: "+1 (555) 123-4567",
|
||||
location: "San Francisco, CA",
|
||||
role: "Admin",
|
||||
department: "Operations",
|
||||
currentPassword: "",
|
||||
newPassword: "",
|
||||
confirmPassword: ""
|
||||
});
|
||||
|
||||
// Notification preferences
|
||||
const [notifications, setNotifications] = useState({
|
||||
emailNotifications: true,
|
||||
pushNotifications: true,
|
||||
bookingAlerts: true,
|
||||
systemUpdates: false,
|
||||
marketingEmails: false
|
||||
});
|
||||
|
||||
const handleSave = () => {
|
||||
setIsEditing(false);
|
||||
// Handle save logic here
|
||||
};
|
||||
|
||||
const tabs = [
|
||||
{ id: "personal", label: "Personal Info", icon: User },
|
||||
{ id: "security", label: "Security", icon: Shield },
|
||||
{ id: "notifications", label: "Notifications", icon: Bell }
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50">
|
||||
<div className="max-w-7xl mx-auto px-6 py-8">
|
||||
{/* Header */}
|
||||
<div className="mb-8">
|
||||
<h1 className="text-3xl font-bold text-gray-900 mb-2">Profile Settings</h1>
|
||||
<p className="text-gray-600">Manage your account settings and preferences</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-4 gap-8">
|
||||
{/* Sidebar Navigation */}
|
||||
<div className="lg:col-span-1">
|
||||
<Card className="bg-white border border-gray-200">
|
||||
<CardContent className="p-6">
|
||||
{/* Profile Summary */}
|
||||
<div className="text-center mb-6">
|
||||
<div className="relative inline-block">
|
||||
<Avatar className="h-20 w-20 mx-auto mb-4">
|
||||
<AvatarImage src="/api/placeholder/80/80" alt="Profile" />
|
||||
<AvatarFallback className="text-lg font-medium">KA</AvatarFallback>
|
||||
</Avatar>
|
||||
<button className="absolute bottom-3 right-0 bg-blue-600 hover:bg-blue-700 text-white rounded-full p-1.5 transition-colors">
|
||||
<Camera className="h-3 w-3" />
|
||||
</button>
|
||||
</div>
|
||||
<h3 className="font-medium text-gray-900">{profileData.firstName} {profileData.lastName}</h3>
|
||||
<p className="text-sm text-gray-500">{profileData.role}</p>
|
||||
<Badge variant="secondary" className="mt-2">
|
||||
{profileData.department}
|
||||
</Badge>
|
||||
</div>
|
||||
|
||||
<Separator className="mb-6" />
|
||||
|
||||
{/* Navigation Tabs */}
|
||||
<nav className="space-y-2">
|
||||
{tabs.map((tab) => {
|
||||
const Icon = tab.icon;
|
||||
return (
|
||||
<button
|
||||
key={tab.id}
|
||||
onClick={() => setActiveTab(tab.id)}
|
||||
className={`w-full flex items-center gap-3 px-3 py-2.5 rounded-lg text-left transition-all duration-200 ${
|
||||
activeTab === tab.id
|
||||
? 'bg-blue-50 text-blue-700 border border-blue-200'
|
||||
: 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'
|
||||
}`}
|
||||
>
|
||||
<Icon className="h-4 w-4" />
|
||||
<span className="font-medium">{tab.label}</span>
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</nav>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* Main Content */}
|
||||
<div className="lg:col-span-3">
|
||||
<motion.div
|
||||
key={activeTab}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.3 }}
|
||||
>
|
||||
{activeTab === "personal" && (
|
||||
<Card className="bg-white border border-gray-200">
|
||||
<CardHeader className="pb-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<CardTitle>Personal Information</CardTitle>
|
||||
<CardDescription>Update your personal details and contact information</CardDescription>
|
||||
</div>
|
||||
<Button
|
||||
variant={isEditing ? "default" : "outline"}
|
||||
onClick={() => isEditing ? handleSave() : setIsEditing(true)}
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
{isEditing ? (
|
||||
<>
|
||||
<Save className="h-4 w-4" />
|
||||
Save Changes
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Edit3 className="h-4 w-4" />
|
||||
Edit Profile
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-6">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="firstName">First Name</Label>
|
||||
<Input
|
||||
id="firstName"
|
||||
value={profileData.firstName}
|
||||
onChange={(e) => setProfileData({...profileData, firstName: e.target.value})}
|
||||
disabled={!isEditing}
|
||||
className="bg-white"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="lastName">Last Name</Label>
|
||||
<Input
|
||||
id="lastName"
|
||||
value={profileData.lastName}
|
||||
onChange={(e) => setProfileData({...profileData, lastName: e.target.value})}
|
||||
disabled={!isEditing}
|
||||
className="bg-white"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="email">Email Address</Label>
|
||||
<div className="relative">
|
||||
<Mail className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 h-4 w-4" />
|
||||
<Input
|
||||
id="email"
|
||||
type="email"
|
||||
value={profileData.email}
|
||||
onChange={(e) => setProfileData({...profileData, email: e.target.value})}
|
||||
disabled={!isEditing}
|
||||
className="pl-10 bg-white"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="phone">Phone Number</Label>
|
||||
<div className="relative">
|
||||
<Phone className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 h-4 w-4" />
|
||||
<Input
|
||||
id="phone"
|
||||
value={profileData.phone}
|
||||
onChange={(e) => setProfileData({...profileData, phone: e.target.value})}
|
||||
disabled={!isEditing}
|
||||
className="pl-10 bg-white"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="location">Location</Label>
|
||||
<div className="relative">
|
||||
<MapPin className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 h-4 w-4" />
|
||||
<Input
|
||||
id="location"
|
||||
value={profileData.location}
|
||||
onChange={(e) => setProfileData({...profileData, location: e.target.value})}
|
||||
disabled={!isEditing}
|
||||
className="pl-10 bg-white"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="role">Role</Label>
|
||||
<Input
|
||||
id="role"
|
||||
value={profileData.role}
|
||||
disabled
|
||||
className="bg-gray-50"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="department">Department</Label>
|
||||
<Input
|
||||
id="department"
|
||||
value={profileData.department}
|
||||
disabled
|
||||
className="bg-gray-50"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{activeTab === "security" && (
|
||||
<Card className="bg-white border border-gray-200">
|
||||
<CardHeader>
|
||||
<CardTitle>Security Settings</CardTitle>
|
||||
<CardDescription>Manage your password and security preferences</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-6">
|
||||
<div className="space-y-4">
|
||||
<h3 className="font-medium text-gray-900">Change Password</h3>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="currentPassword">Current Password</Label>
|
||||
<div className="relative">
|
||||
<Input
|
||||
id="currentPassword"
|
||||
type={showCurrentPassword ? "text" : "password"}
|
||||
value={profileData.currentPassword}
|
||||
onChange={(e) => setProfileData({...profileData, currentPassword: e.target.value})}
|
||||
className="pr-10 bg-white"
|
||||
placeholder="Enter current password"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowCurrentPassword(!showCurrentPassword)}
|
||||
className="absolute right-3 top-1/2 transform -translate-y-1/2 text-gray-400 hover:text-gray-600"
|
||||
>
|
||||
{showCurrentPassword ? <EyeOff className="h-4 w-4" /> : <Eye className="h-4 w-4" />}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="newPassword">New Password</Label>
|
||||
<div className="relative">
|
||||
<Input
|
||||
id="newPassword"
|
||||
type={showNewPassword ? "text" : "password"}
|
||||
value={profileData.newPassword}
|
||||
onChange={(e) => setProfileData({...profileData, newPassword: e.target.value})}
|
||||
className="pr-10 bg-white"
|
||||
placeholder="Enter new password"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowNewPassword(!showNewPassword)}
|
||||
className="absolute right-3 top-1/2 transform -translate-y-1/2 text-gray-400 hover:text-gray-600"
|
||||
>
|
||||
{showNewPassword ? <EyeOff className="h-4 w-4" /> : <Eye className="h-4 w-4" />}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="confirmPassword">Confirm New Password</Label>
|
||||
<div className="relative">
|
||||
<Input
|
||||
id="confirmPassword"
|
||||
type={showConfirmPassword ? "text" : "password"}
|
||||
value={profileData.confirmPassword}
|
||||
onChange={(e) => setProfileData({...profileData, confirmPassword: e.target.value})}
|
||||
className="pr-10 bg-white"
|
||||
placeholder="Confirm new password"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowConfirmPassword(!showConfirmPassword)}
|
||||
className="absolute right-3 top-1/2 transform -translate-y-1/2 text-gray-400 hover:text-gray-600"
|
||||
>
|
||||
{showConfirmPassword ? <EyeOff className="h-4 w-4" /> : <Eye className="h-4 w-4" />}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Button className="bg-blue-600 hover:bg-blue-700 text-white">
|
||||
Update Password
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<Separator />
|
||||
|
||||
<div className="space-y-4">
|
||||
<h3 className="font-medium text-gray-900">Security Information</h3>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div className="bg-gray-50 p-4 rounded-lg">
|
||||
<h4 className="font-medium text-gray-900 mb-1">Last Login</h4>
|
||||
<p className="text-sm text-gray-600">Today at 2:34 PM</p>
|
||||
</div>
|
||||
<div className="bg-gray-50 p-4 rounded-lg">
|
||||
<h4 className="font-medium text-gray-900 mb-1">Account Created</h4>
|
||||
<p className="text-sm text-gray-600">March 15, 2024</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{activeTab === "notifications" && (
|
||||
<Card className="bg-white border border-gray-200">
|
||||
<CardHeader>
|
||||
<CardTitle>Notification Preferences</CardTitle>
|
||||
<CardDescription>Choose how you want to receive notifications</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-6">
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h4 className="font-medium text-gray-900">Email Notifications</h4>
|
||||
<p className="text-sm text-gray-500">Receive notifications via email</p>
|
||||
</div>
|
||||
<Switch
|
||||
checked={notifications.emailNotifications}
|
||||
onCheckedChange={(checked) => setNotifications({...notifications, emailNotifications: checked})}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h4 className="font-medium text-gray-900">Push Notifications</h4>
|
||||
<p className="text-sm text-gray-500">Receive push notifications in browser</p>
|
||||
</div>
|
||||
<Switch
|
||||
checked={notifications.pushNotifications}
|
||||
onCheckedChange={(checked) => setNotifications({...notifications, pushNotifications: checked})}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h4 className="font-medium text-gray-900">Booking Alerts</h4>
|
||||
<p className="text-sm text-gray-500">Get notified about new bookings and changes</p>
|
||||
</div>
|
||||
<Switch
|
||||
checked={notifications.bookingAlerts}
|
||||
onCheckedChange={(checked) => setNotifications({...notifications, bookingAlerts: checked})}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h4 className="font-medium text-gray-900">System Updates</h4>
|
||||
<p className="text-sm text-gray-500">Receive notifications about system updates</p>
|
||||
</div>
|
||||
<Switch
|
||||
checked={notifications.systemUpdates}
|
||||
onCheckedChange={(checked) => setNotifications({...notifications, systemUpdates: checked})}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h4 className="font-medium text-gray-900">Marketing Emails</h4>
|
||||
<p className="text-sm text-gray-500">Receive promotional emails and updates</p>
|
||||
</div>
|
||||
<Switch
|
||||
checked={notifications.marketingEmails}
|
||||
onCheckedChange={(checked) => setNotifications({...notifications, marketingEmails: checked})}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Separator />
|
||||
|
||||
<div className="pt-4">
|
||||
<Button className="bg-blue-600 hover:bg-blue-700 text-white">
|
||||
Save Preferences
|
||||
</Button>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -597,6 +597,10 @@
|
||||
bottom: calc(var(--spacing) * 1);
|
||||
}
|
||||
|
||||
.bottom-3 {
|
||||
bottom: calc(var(--spacing) * 3);
|
||||
}
|
||||
|
||||
.bottom-\[7\.01\%\] {
|
||||
bottom: 7.01%;
|
||||
}
|
||||
@@ -753,6 +757,10 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
.inline-block {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.inline-flex {
|
||||
display: inline-flex;
|
||||
}
|
||||
@@ -874,6 +882,10 @@
|
||||
height: calc(var(--spacing) * 16);
|
||||
}
|
||||
|
||||
.h-20 {
|
||||
height: calc(var(--spacing) * 20);
|
||||
}
|
||||
|
||||
.h-32 {
|
||||
height: calc(var(--spacing) * 32);
|
||||
}
|
||||
@@ -1249,6 +1261,10 @@
|
||||
gap: calc(var(--spacing) * 6);
|
||||
}
|
||||
|
||||
.gap-8 {
|
||||
gap: calc(var(--spacing) * 8);
|
||||
}
|
||||
|
||||
:where(.space-y-0 > :not(:last-child)) {
|
||||
--tw-space-y-reverse: 0;
|
||||
margin-block-start: calc(calc(var(--spacing) * 0) * var(--tw-space-y-reverse));
|
||||
@@ -1685,6 +1701,10 @@
|
||||
padding: calc(var(--spacing) * 1);
|
||||
}
|
||||
|
||||
.p-1\.5 {
|
||||
padding: calc(var(--spacing) * 1.5);
|
||||
}
|
||||
|
||||
.p-2 {
|
||||
padding: calc(var(--spacing) * 2);
|
||||
}
|
||||
@@ -1737,6 +1757,10 @@
|
||||
padding-block: calc(var(--spacing) * 2);
|
||||
}
|
||||
|
||||
.py-2\.5 {
|
||||
padding-block: calc(var(--spacing) * 2.5);
|
||||
}
|
||||
|
||||
.py-3 {
|
||||
padding-block: calc(var(--spacing) * 3);
|
||||
}
|
||||
@@ -2764,6 +2788,22 @@
|
||||
padding-left: calc(var(--spacing) * 8);
|
||||
}
|
||||
|
||||
.data-\[orientation\=horizontal\]\:h-px[data-orientation="horizontal"] {
|
||||
height: 1px;
|
||||
}
|
||||
|
||||
.data-\[orientation\=horizontal\]\:w-full[data-orientation="horizontal"] {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.data-\[orientation\=vertical\]\:h-full[data-orientation="vertical"] {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.data-\[orientation\=vertical\]\:w-px[data-orientation="vertical"] {
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
.data-\[placeholder\]\:text-muted-foreground[data-placeholder] {
|
||||
color: var(--muted-foreground);
|
||||
}
|
||||
@@ -2968,12 +3008,30 @@
|
||||
}
|
||||
}
|
||||
|
||||
@media (width >= 64rem) {
|
||||
.lg\:col-span-1 {
|
||||
grid-column: span 1 / span 1;
|
||||
}
|
||||
}
|
||||
|
||||
@media (width >= 64rem) {
|
||||
.lg\:col-span-3 {
|
||||
grid-column: span 3 / span 3;
|
||||
}
|
||||
}
|
||||
|
||||
@media (width >= 64rem) {
|
||||
.lg\:grid-cols-2 {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
@media (width >= 64rem) {
|
||||
.lg\:grid-cols-4 {
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
.dark\:border-input:is(.dark *) {
|
||||
border-color: var(--input);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
import { createRoot } from "react-dom/client";
|
||||
import App from "./App.tsx";
|
||||
import App from "./App";
|
||||
import "./index.css";
|
||||
|
||||
createRoot(document.getElementById("root")!).render(<App />);
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
@import "tailwindcss";
|
||||
@custom-variant dark (&:is(.dark *));
|
||||
|
||||
:root {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
import { defineConfig } from 'vite';
|
||||
import react from '@vitejs/plugin-react-swc';
|
||||
import path from 'path';
|
||||
import * as path from 'path';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
@@ -56,7 +56,7 @@
|
||||
outDir: 'build',
|
||||
},
|
||||
server: {
|
||||
port: 3000,
|
||||
port: 4001,
|
||||
open: true,
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user