modula-ui 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -0
- package/bin/run.js +86 -0
- package/package.json +71 -0
- package/public/avatars/avatar1.png +0 -0
- package/public/avatars/avatar2.png +0 -0
- package/public/avatars/avatar3.png +0 -0
- package/public/avatars/avatar4.png +0 -0
- package/public/avatars/sources.md +34 -0
- package/public/file.svg +1 -0
- package/public/globe.svg +1 -0
- package/public/next.svg +1 -0
- package/public/vercel.svg +1 -0
- package/public/window.svg +1 -0
- package/registry.json +12 -0
- package/src/app/favicon.ico +0 -0
- package/src/app/globals.css +126 -0
- package/src/app/layout.js +29 -0
- package/src/app/page.js +50 -0
- package/src/app/patterns/page.js +50 -0
- package/src/components/CodeCard.jsx +16 -0
- package/src/components/CopyButton.jsx +31 -0
- package/src/components/Header.jsx +24 -0
- package/src/components/Logo.jsx +64 -0
- package/src/components/MobileOverlay.jsx +10 -0
- package/src/components/PreviewCard.jsx +98 -0
- package/src/components/Sidebar.jsx +47 -0
- package/src/components/ui/avatar.jsx +47 -0
- package/src/components/ui/badge.jsx +44 -0
- package/src/components/ui/button.jsx +56 -0
- package/src/components/ui/calendar.jsx +178 -0
- package/src/components/ui/card.jsx +101 -0
- package/src/components/ui/chart.jsx +314 -0
- package/src/components/ui/checkbox.jsx +30 -0
- package/src/components/ui/dropdown-menu.jsx +223 -0
- package/src/components/ui/input.jsx +24 -0
- package/src/components/ui/navigation-menu.jsx +152 -0
- package/src/components/ui/popover.jsx +47 -0
- package/src/components/ui/progress.jsx +29 -0
- package/src/components/ui/scroll-area.jsx +51 -0
- package/src/components/ui/select.jsx +168 -0
- package/src/components/ui/separator.jsx +27 -0
- package/src/components/ui/sheet.jsx +140 -0
- package/src/components/ui/sidebar.jsx +682 -0
- package/src/components/ui/skeleton.jsx +15 -0
- package/src/components/ui/slider.jsx +56 -0
- package/src/components/ui/tooltip.jsx +55 -0
- package/src/data/componentData.js +12 -0
- package/src/hooks/use-mobile.js +19 -0
- package/src/lib/utils.js +6 -0
- package/src/library/components/Alert.jsx +27 -0
- package/src/library/components/Badge.jsx +19 -0
- package/src/library/components/Button.jsx +31 -0
- package/src/library/components/Card.jsx +25 -0
- package/src/library/components/Input.jsx +35 -0
- package/src/library/components/Modal.jsx +26 -0
- package/src/library/components/Textarea.jsx +15 -0
- package/src/library/components/Toggle.jsx +16 -0
- package/src/library/pages/FitnessPage/FitnessPage.jsx +519 -0
- package/src/library/pages/FitnessPage/index.jsx +12 -0
- package/src/library/pages/GroupChat/GroupChat.jsx +275 -0
- package/src/library/pages/GroupChat/data.js +203 -0
- package/src/library/pages/GroupChat/index.jsx +12 -0
- package/src/library/pages/ReservationsOverview/ReservationsOverviewPage.jsx +225 -0
- package/src/library/pages/ReservationsOverview/index.jsx +12 -0
- package/src/library/pages/VideoConference/VideoConferencePage.jsx +334 -0
- package/src/library/pages/VideoConference/index.jsx +12 -0
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
import React from "react";
|
|
3
|
+
import {
|
|
4
|
+
Sidebar,
|
|
5
|
+
SidebarContent,
|
|
6
|
+
SidebarHeader,
|
|
7
|
+
SidebarMenu,
|
|
8
|
+
SidebarMenuItem,
|
|
9
|
+
SidebarMenuButton,
|
|
10
|
+
SidebarProvider,
|
|
11
|
+
SidebarInset,
|
|
12
|
+
SidebarFooter,
|
|
13
|
+
} from "@/components/ui/sidebar";
|
|
14
|
+
import { Input } from "@/components/ui/input";
|
|
15
|
+
import { Button } from "@/components/ui/button";
|
|
16
|
+
import { Card, CardContent } from "@/components/ui/card";
|
|
17
|
+
import { Slider } from "@/components/ui/slider";
|
|
18
|
+
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
|
19
|
+
import {
|
|
20
|
+
Search,
|
|
21
|
+
SlidersHorizontal,
|
|
22
|
+
Home,
|
|
23
|
+
Grid,
|
|
24
|
+
CalendarCheck,
|
|
25
|
+
Heart,
|
|
26
|
+
MapPin,
|
|
27
|
+
Plus,
|
|
28
|
+
Star,
|
|
29
|
+
Coffee,
|
|
30
|
+
Beer,
|
|
31
|
+
Pizza,
|
|
32
|
+
Soup,
|
|
33
|
+
Sandwich,
|
|
34
|
+
Martini,
|
|
35
|
+
Carrot,
|
|
36
|
+
Fish,
|
|
37
|
+
} from "lucide-react";
|
|
38
|
+
|
|
39
|
+
const categories = [
|
|
40
|
+
{ name: "Italian", icon: Pizza, color: "bg-gray-100" },
|
|
41
|
+
{ name: "Asian", icon: Soup, color: "bg-gray-100" },
|
|
42
|
+
{ name: "Bars", icon: Martini, color: "bg-red-50" },
|
|
43
|
+
{ name: "Burgers", icon: Sandwich, color: "bg-gray-100" },
|
|
44
|
+
{ name: "Cafe", icon: Coffee, color: "bg-gray-100" },
|
|
45
|
+
{ name: "Pubs", icon: Beer, color: "bg-gray-100" },
|
|
46
|
+
{ name: "Vegan", icon: Carrot, color: "bg-gray-100" },
|
|
47
|
+
{ name: "Seafood", icon: Fish, color: "bg-green-50" },
|
|
48
|
+
{ name: "More", icon: Plus, color: "bg-gray-100" },
|
|
49
|
+
];
|
|
50
|
+
|
|
51
|
+
const restaurants = [
|
|
52
|
+
{
|
|
53
|
+
id: 1,
|
|
54
|
+
name: "Molon Lave",
|
|
55
|
+
type: "Asian Kitchen",
|
|
56
|
+
rating: 4.7,
|
|
57
|
+
price: 30,
|
|
58
|
+
distance: 0.2,
|
|
59
|
+
image: "https://images.unsplash.com/photo-1540189549336-e6e99c3679fe?q=80&w=1000&auto=format&fit=crop",
|
|
60
|
+
bgColor: "bg-pink-50",
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
id: 2,
|
|
64
|
+
name: "Lureme",
|
|
65
|
+
type: "Cocktail Bar",
|
|
66
|
+
rating: 4.8,
|
|
67
|
+
reviews: 50,
|
|
68
|
+
price: 50,
|
|
69
|
+
distance: 1.2,
|
|
70
|
+
image: "https://images.unsplash.com/photo-1514362545857-3bc16549766b?q=80&w=1000&auto=format&fit=crop",
|
|
71
|
+
isSpecial: true,
|
|
72
|
+
bgColor: "bg-orange-50",
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
id: 3,
|
|
76
|
+
name: "Boston Seafood",
|
|
77
|
+
type: "SeaFood",
|
|
78
|
+
rating: 3.9,
|
|
79
|
+
price: 89,
|
|
80
|
+
distance: 3.1,
|
|
81
|
+
image: "https://images.unsplash.com/photo-1565299624946-b28f40a0ae38?q=80&w=1000&auto=format&fit=crop",
|
|
82
|
+
bgColor: "bg-green-50",
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
id: 4,
|
|
86
|
+
name: "Powerhouse",
|
|
87
|
+
type: "Vegan",
|
|
88
|
+
rating: 4.2,
|
|
89
|
+
price: 28,
|
|
90
|
+
distance: 0.6,
|
|
91
|
+
image: "https://images.unsplash.com/photo-1512621776951-a57141f2eefd?q=80&w=1000&auto=format&fit=crop",
|
|
92
|
+
bgColor: "bg-yellow-50",
|
|
93
|
+
},
|
|
94
|
+
];
|
|
95
|
+
|
|
96
|
+
export default function ReservationsOverviewPage() {
|
|
97
|
+
return (
|
|
98
|
+
// <SidebarProvider>
|
|
99
|
+
<div className="flex h-screen w-full bg-gray-50 overflow-hidden">
|
|
100
|
+
|
|
101
|
+
{/* <SidebarInset className="flex-1 bg-white p-8 overflow-y-auto"> */}
|
|
102
|
+
<header className="flex items-center justify-between gap-8 mb-12">
|
|
103
|
+
<div className="relative flex-1 max-w-md">
|
|
104
|
+
<Search className="absolute left-0 top-1/2 h-5 w-5 -translate-y-1/2 text-gray-400" />
|
|
105
|
+
<Input
|
|
106
|
+
placeholder="Enter your search request..."
|
|
107
|
+
className="border-0 border-b border-gray-200 bg-transparent pl-8 rounded-none focus-visible:ring-0 px-0 placeholder:text-gray-400"
|
|
108
|
+
/>
|
|
109
|
+
</div>
|
|
110
|
+
<div className="flex items-center gap-4">
|
|
111
|
+
<Button variant="ghost" size="icon">
|
|
112
|
+
<SlidersHorizontal className="h-5 w-5" />
|
|
113
|
+
</Button>
|
|
114
|
+
<Button className="bg-[#1a1a1a] text-white hover:bg-[#333] rounded-xl px-6">
|
|
115
|
+
Go to Premium
|
|
116
|
+
</Button>
|
|
117
|
+
</div>
|
|
118
|
+
</header>
|
|
119
|
+
|
|
120
|
+
{/* Title Section */}
|
|
121
|
+
<div className="flex items-end justify-between mb-12">
|
|
122
|
+
<div>
|
|
123
|
+
<h1 className="text-4xl font-bold text-[#1a1a1a] mb-2">
|
|
124
|
+
Find the best place
|
|
125
|
+
</h1>
|
|
126
|
+
<p className="text-gray-500">
|
|
127
|
+
<span className="font-semibold text-[#1a1a1a]">249 restaurants</span>,
|
|
128
|
+
choose yours
|
|
129
|
+
</p>
|
|
130
|
+
</div>
|
|
131
|
+
<div className="flex gap-8">
|
|
132
|
+
<div className="text-center">
|
|
133
|
+
<p className="text-2xl font-bold text-[#1a1a1a]">94</p>
|
|
134
|
+
<p className="text-sm text-gray-400">Specials</p>
|
|
135
|
+
</div>
|
|
136
|
+
<div className="text-center">
|
|
137
|
+
<p className="text-2xl font-bold text-[#1a1a1a]">23</p>
|
|
138
|
+
<p className="text-sm text-gray-400">Delivery</p>
|
|
139
|
+
</div>
|
|
140
|
+
</div>
|
|
141
|
+
</div>
|
|
142
|
+
|
|
143
|
+
{/* Categories */}
|
|
144
|
+
<div className="flex gap-8 overflow-x-auto pb-8 scrollbar-hide mb-8">
|
|
145
|
+
{categories.map((category, index) => (
|
|
146
|
+
<div key={index} className="flex flex-col items-center gap-3 min-w-[80px] cursor-pointer group">
|
|
147
|
+
<div className={`flex h-16 w-16 items-center justify-center rounded-full ${category.color} transition-transform group-hover:scale-110`}>
|
|
148
|
+
<category.icon className="h-6 w-6 text-[#1a1a1a]" />
|
|
149
|
+
</div>
|
|
150
|
+
<span className="text-xs font-medium text-gray-500 uppercase tracking-wide group-hover:text-[#1a1a1a]">
|
|
151
|
+
{category.name}
|
|
152
|
+
</span>
|
|
153
|
+
</div>
|
|
154
|
+
))}
|
|
155
|
+
</div>
|
|
156
|
+
|
|
157
|
+
{/* New Restaurants & Slider */}
|
|
158
|
+
<div className="flex items-center justify-between mb-8">
|
|
159
|
+
<h2 className="text-2xl font-bold text-[#1a1a1a]">New restaurants</h2>
|
|
160
|
+
<div className="flex flex-col items-end gap-2 w-48">
|
|
161
|
+
<span className="text-xs font-medium text-gray-500 bg-white px-2 py-1 rounded-full shadow-sm border border-gray-100">4 km</span>
|
|
162
|
+
<Slider defaultValue={[33]} max={100} step={1} className="w-full" />
|
|
163
|
+
</div>
|
|
164
|
+
</div>
|
|
165
|
+
|
|
166
|
+
{/* Cards Grid */}
|
|
167
|
+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
|
168
|
+
{restaurants.map((restaurant) => (
|
|
169
|
+
<Card
|
|
170
|
+
key={restaurant.id}
|
|
171
|
+
className={`border-0 shadow-none ${restaurant.bgColor} rounded-3xl overflow-hidden group cursor-pointer transition-all hover:shadow-lg`}
|
|
172
|
+
>
|
|
173
|
+
<CardContent className="p-4 pb-0">
|
|
174
|
+
<div className="relative aspect-[4/3] overflow-hidden rounded-2xl mb-4">
|
|
175
|
+
<img
|
|
176
|
+
src={restaurant.image}
|
|
177
|
+
alt={restaurant.name}
|
|
178
|
+
className="h-full w-full object-cover transition-transform duration-500 group-hover:scale-110"
|
|
179
|
+
/>
|
|
180
|
+
{restaurant.isSpecial && (
|
|
181
|
+
<div className="absolute bottom-2 right-2 flex items-center gap-1 rounded-full bg-white px-2 py-1 text-xs font-bold shadow-sm">
|
|
182
|
+
<Avatar className="h-4 w-4">
|
|
183
|
+
<AvatarImage src="https://images.unsplash.com/photo-1494790108377-be9c29b29330?q=80&w=200&auto=format&fit=crop" />
|
|
184
|
+
<AvatarFallback>U</AvatarFallback>
|
|
185
|
+
</Avatar>
|
|
186
|
+
<span>{restaurant.rating}</span>
|
|
187
|
+
<Star className="h-3 w-3 fill-black text-black" />
|
|
188
|
+
</div>
|
|
189
|
+
)}
|
|
190
|
+
</div>
|
|
191
|
+
<div className="text-center mb-4">
|
|
192
|
+
<h3 className="font-bold text-lg text-[#1a1a1a] mb-1">
|
|
193
|
+
{restaurant.name}
|
|
194
|
+
</h3>
|
|
195
|
+
<p className="text-sm text-gray-500">{restaurant?.type}</p>
|
|
196
|
+
</div>
|
|
197
|
+
</CardContent>
|
|
198
|
+
{/* <CardFooter className="flex justify-between px-8 pb-6"> */}
|
|
199
|
+
<div className="text-center">
|
|
200
|
+
<div className="flex items-center justify-center gap-1 mb-1">
|
|
201
|
+
{restaurant.isSpecial ? (
|
|
202
|
+
<Star className="h-3 w-3 fill-black text-black" />
|
|
203
|
+
) : (
|
|
204
|
+
<Star className="h-3 w-3 fill-black text-black" />
|
|
205
|
+
)}
|
|
206
|
+
</div>
|
|
207
|
+
<p className="text-lg font-bold text-[#1a1a1a]">{restaurant.isSpecial ? restaurant.rating : restaurant.rating}</p>
|
|
208
|
+
</div>
|
|
209
|
+
<div className="text-center">
|
|
210
|
+
<p className="text-xs text-gray-400 mb-1">$$$</p>
|
|
211
|
+
<p className="text-lg font-bold text-[#1a1a1a]">{restaurant.price}</p>
|
|
212
|
+
</div>
|
|
213
|
+
<div className="text-center">
|
|
214
|
+
<p className="text-xs text-gray-400 mb-1">km</p>
|
|
215
|
+
<p className="text-lg font-bold text-[#1a1a1a]">{restaurant.distance}</p>
|
|
216
|
+
</div>
|
|
217
|
+
{/* </CardFooter> */}
|
|
218
|
+
</Card>
|
|
219
|
+
))}
|
|
220
|
+
</div>
|
|
221
|
+
{/* </SidebarInset> */}
|
|
222
|
+
</div>
|
|
223
|
+
// </SidebarProvider>
|
|
224
|
+
);
|
|
225
|
+
}
|
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import React, { useState } from 'react';
|
|
4
|
+
import {
|
|
5
|
+
Mic,
|
|
6
|
+
MicOff,
|
|
7
|
+
Video,
|
|
8
|
+
VideoOff,
|
|
9
|
+
PhoneOff,
|
|
10
|
+
MoreVertical,
|
|
11
|
+
Smile,
|
|
12
|
+
Hand,
|
|
13
|
+
Users,
|
|
14
|
+
MessageSquare,
|
|
15
|
+
ChevronLeft,
|
|
16
|
+
Link as LinkIcon,
|
|
17
|
+
UserPlus,
|
|
18
|
+
Maximize2,
|
|
19
|
+
Pin,
|
|
20
|
+
ChevronDown
|
|
21
|
+
} from 'lucide-react';
|
|
22
|
+
import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar";
|
|
23
|
+
import { cn } from "@/lib/utils";
|
|
24
|
+
import { motion, AnimatePresence } from "framer-motion";
|
|
25
|
+
|
|
26
|
+
export default function VideoConferencePage() {
|
|
27
|
+
const [isMicOn, setIsMicOn] = useState(true);
|
|
28
|
+
const [isVideoOn, setIsVideoOn] = useState(true);
|
|
29
|
+
const [activeTab, setActiveTab] = useState('participants');
|
|
30
|
+
|
|
31
|
+
const participants = [
|
|
32
|
+
{ id: 1, name: "Ahmad Septimus (You)", email: "ahmd_septimus@mail.com", image: "https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?q=80&w=2070&auto=format&fit=crop", isMuted: false, isVideoOn: true, isMe: true },
|
|
33
|
+
{ id: 2, name: "Lydia Stanton", email: "ldya_stant@mail.com", image: "https://images.unsplash.com/photo-1494790108377-be9c29b29330?q=80&w=2070&auto=format&fit=crop", isMuted: false, isVideoOn: true },
|
|
34
|
+
{ id: 3, name: "Maria Carder", email: "marcarder@mail.com", image: "https://images.unsplash.com/photo-1544005313-94ddf0286df2?q=80&w=2070&auto=format&fit=crop", isMuted: true, isVideoOn: true },
|
|
35
|
+
{ id: 4, name: "Jordyn Bergson", email: "jordynberg@mail.com", image: "https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?q=80&w=2070&auto=format&fit=crop", isMuted: true, isVideoOn: true },
|
|
36
|
+
{ id: 5, name: "Maria Mango", email: "mariamango@mail.com", image: "https://i.pravatar.cc/150?u=5", isMuted: true, isVideoOn: true },
|
|
37
|
+
];
|
|
38
|
+
|
|
39
|
+
const containerVariants = {
|
|
40
|
+
hidden: { opacity: 0 },
|
|
41
|
+
visible: {
|
|
42
|
+
opacity: 1,
|
|
43
|
+
transition: {
|
|
44
|
+
staggerChildren: 0.1
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const itemVariants = {
|
|
50
|
+
hidden: { opacity: 0, y: 20 },
|
|
51
|
+
visible: { opacity: 1, y: 0 }
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<motion.div
|
|
56
|
+
initial="hidden"
|
|
57
|
+
animate="visible"
|
|
58
|
+
variants={containerVariants}
|
|
59
|
+
className="flex flex-col lg:flex-row h-full w-full bg-[#1C1C1E] text-white overflow-hidden font-sans"
|
|
60
|
+
>
|
|
61
|
+
{/* Main Content Area */}
|
|
62
|
+
<div className="flex-1 flex flex-col p-2 sm:p-4 gap-2 sm:gap-4 overflow-hidden">
|
|
63
|
+
|
|
64
|
+
{/* Header */}
|
|
65
|
+
<motion.header variants={itemVariants} className="flex flex-wrap items-center justify-between px-2 gap-2 shrink-0">
|
|
66
|
+
<div className="flex items-center gap-4">
|
|
67
|
+
<motion.button whileHover={{ scale: 1.1 }} whileTap={{ scale: 0.95 }} className="p-2 hover:bg-white/10 rounded-full transition-colors">
|
|
68
|
+
<ChevronLeft className="w-5 h-5 text-gray-400" />
|
|
69
|
+
</motion.button>
|
|
70
|
+
<div>
|
|
71
|
+
<h1 className="text-sm sm:text-base font-semibold">Design Team Reporting</h1>
|
|
72
|
+
<p className="text-xs text-gray-400">09:45, 03 Sep 2025</p>
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
|
|
76
|
+
<div className="hidden sm:flex items-center gap-2 bg-[#2C2C2E] px-3 py-1.5 rounded-full">
|
|
77
|
+
<motion.div
|
|
78
|
+
animate={{ opacity: [1, 0.5, 1] }}
|
|
79
|
+
transition={{ duration: 2, repeat: Infinity }}
|
|
80
|
+
className="w-2 h-2 bg-red-500 rounded-full"
|
|
81
|
+
/>
|
|
82
|
+
<span className="text-xs font-medium">40:12:32</span>
|
|
83
|
+
</div>
|
|
84
|
+
|
|
85
|
+
<div className="flex items-center gap-3">
|
|
86
|
+
<div className="hidden md:flex items-center gap-2 bg-[#2C2C2E] px-3 py-1.5 rounded-full text-xs text-gray-400 max-w-[200px] truncate">
|
|
87
|
+
<LinkIcon className="w-3 h-3" />
|
|
88
|
+
<span className="truncate">https://meet.us/j/123456789?pwd=abc123xyz...</span>
|
|
89
|
+
</div>
|
|
90
|
+
<motion.button
|
|
91
|
+
whileHover={{ scale: 1.05 }}
|
|
92
|
+
whileTap={{ scale: 0.95 }}
|
|
93
|
+
className="flex items-center gap-2 bg-[#8B5CF6] hover:bg-[#7C3AED] text-white px-4 py-1.5 rounded-full text-sm font-medium transition-colors"
|
|
94
|
+
>
|
|
95
|
+
<UserPlus className="w-4 h-4" />
|
|
96
|
+
Invite People
|
|
97
|
+
</motion.button>
|
|
98
|
+
</div>
|
|
99
|
+
</motion.header>
|
|
100
|
+
|
|
101
|
+
{/* Main Stage */}
|
|
102
|
+
<main className="flex-1 flex flex-col gap-4 relative">
|
|
103
|
+
{/* Active Speaker */}
|
|
104
|
+
<motion.div variants={itemVariants} className="flex-1 min-h-0 relative rounded-3xl overflow-hidden bg-zinc-900 border border-white/5 group">
|
|
105
|
+
<div className='h-64'>
|
|
106
|
+
<img
|
|
107
|
+
src="https://images.unsplash.com/photo-1573496359142-b8d87734a5a2?q=80&w=2576&auto=format&fit=crop"
|
|
108
|
+
alt="Active Speaker"
|
|
109
|
+
className="w-full h-full object-cover object-[20%_20%]"
|
|
110
|
+
/>
|
|
111
|
+
</div>
|
|
112
|
+
|
|
113
|
+
{/* Top Right Controls */}
|
|
114
|
+
<div className="absolute top-4 right-4 flex gap-2 opacity-0 group-hover:opacity-100 transition-opacity duration-300">
|
|
115
|
+
<motion.button whileHover={{ scale: 1.1 }} className="p-2 bg-black/40 hover:bg-black/60 backdrop-blur-md rounded-full text-white">
|
|
116
|
+
<Pin className="w-4 h-4" />
|
|
117
|
+
</motion.button>
|
|
118
|
+
<motion.button whileHover={{ scale: 1.1 }} className="p-2 bg-black/40 hover:bg-black/60 backdrop-blur-md rounded-full text-white">
|
|
119
|
+
<Maximize2 className="w-4 h-4" />
|
|
120
|
+
</motion.button>
|
|
121
|
+
</div>
|
|
122
|
+
|
|
123
|
+
{/* Bottom Left Label */}
|
|
124
|
+
<div className="absolute bottom-4 left-4 flex items-center gap-2 bg-black/40 backdrop-blur-md px-3 py-1.5 rounded-full">
|
|
125
|
+
<UserPlus className="w-3 h-3 text-white" /> {/* Placeholder for user icon */}
|
|
126
|
+
<span className="text-sm font-medium">Lydia Staton</span>
|
|
127
|
+
</div>
|
|
128
|
+
|
|
129
|
+
{/* Bottom Right Audio Visualizer (Mock) */}
|
|
130
|
+
<div className="absolute bottom-4 right-4 bg-[#8B5CF6] p-2 rounded-full">
|
|
131
|
+
<div className="flex gap-0.5 items-center h-4">
|
|
132
|
+
{[1, 2, 3, 2, 1].map((h, i) => (
|
|
133
|
+
<motion.div
|
|
134
|
+
key={i}
|
|
135
|
+
animate={{ height: [h * 2, h * 6, h * 2] }}
|
|
136
|
+
transition={{ duration: 0.5 + i * 0.1, repeat: Infinity, ease: "easeInOut" }}
|
|
137
|
+
className="w-0.5 bg-white rounded-full"
|
|
138
|
+
/>
|
|
139
|
+
))}
|
|
140
|
+
</div>
|
|
141
|
+
</div>
|
|
142
|
+
</motion.div>
|
|
143
|
+
</main>
|
|
144
|
+
{/* Participants Grid (Bottom) */}
|
|
145
|
+
<motion.div variants={itemVariants} className="shrink-0 h-auto sm:h-40 grid grid-cols-2 lg:grid-cols-4 gap-2 sm:gap-4">
|
|
146
|
+
{/* Participant 1 */}
|
|
147
|
+
<div className="relative rounded-2xl overflow-hidden bg-zinc-800 group">
|
|
148
|
+
<img src="https://images.unsplash.com/photo-1580489944761-15a19d654956?q=80&w=2561&auto=format&fit=crop" className="w-full h-full object-cover transition-transform duration-500 group-hover:scale-110" alt="Participant" />
|
|
149
|
+
<div className="absolute bottom-2 left-2 bg-black/40 backdrop-blur-md px-2 py-1 rounded-lg text-xs font-medium">
|
|
150
|
+
Maria Mango
|
|
151
|
+
</div>
|
|
152
|
+
<div className="absolute bottom-2 right-2 bg-black/40 backdrop-blur-md p-1.5 rounded-full">
|
|
153
|
+
<MicOff className="w-3 h-3 text-red-400" />
|
|
154
|
+
</div>
|
|
155
|
+
</div>
|
|
156
|
+
{/* Participant 2 */}
|
|
157
|
+
<div className="relative rounded-2xl overflow-hidden bg-zinc-800 group">
|
|
158
|
+
<img src="https://images.unsplash.com/photo-1535713875002-d1d0cf377fde?q=80&w=2680&auto=format&fit=crop" className="w-full h-full object-cover transition-transform duration-500 group-hover:scale-110" alt="Participant" />
|
|
159
|
+
<div className="absolute bottom-2 left-2 bg-black/40 backdrop-blur-md px-2 py-1 rounded-lg text-xs font-medium">
|
|
160
|
+
Jordyn Bergson
|
|
161
|
+
</div>
|
|
162
|
+
<div className="absolute bottom-2 right-2 bg-black/40 backdrop-blur-md p-1.5 rounded-full">
|
|
163
|
+
<MicOff className="w-3 h-3 text-red-400" />
|
|
164
|
+
</div>
|
|
165
|
+
</div>
|
|
166
|
+
{/* Participant 3 */}
|
|
167
|
+
<div className="relative rounded-2xl overflow-hidden bg-zinc-800 group">
|
|
168
|
+
<img src="https://images.unsplash.com/photo-1438761681033-6461ffad8d80?q=80&w=2670&auto=format&fit=crop" className="w-full h-full object-cover transition-transform duration-500 group-hover:scale-110" alt="Participant" />
|
|
169
|
+
<div className="absolute bottom-2 left-2 bg-black/40 backdrop-blur-md px-2 py-1 rounded-lg text-xs font-medium">
|
|
170
|
+
Maria Carder
|
|
171
|
+
</div>
|
|
172
|
+
<div className="absolute bottom-2 right-2 bg-black/40 backdrop-blur-md p-1.5 rounded-full">
|
|
173
|
+
<MicOff className="w-3 h-3 text-red-400" />
|
|
174
|
+
</div>
|
|
175
|
+
</div>
|
|
176
|
+
{/* Participant 4 (You) */}
|
|
177
|
+
<div className="relative rounded-2xl overflow-hidden bg-zinc-800 border-2 border-[#8B5CF6] group">
|
|
178
|
+
<img src="https://images.unsplash.com/photo-1599566150163-29194dcaad36?q=80&w=2574&auto=format&fit=crop" className="w-full h-full object-cover transition-transform duration-500 group-hover:scale-110" alt="Participant" />
|
|
179
|
+
<div className="absolute bottom-2 left-2 bg-black/40 backdrop-blur-md px-2 py-1 rounded-lg text-xs font-medium">
|
|
180
|
+
You
|
|
181
|
+
</div>
|
|
182
|
+
<div className="absolute bottom-2 right-2 bg-[#8B5CF6] p-1.5 rounded-full">
|
|
183
|
+
<div className="flex gap-0.5 items-center h-3">
|
|
184
|
+
{[1, 2, 1].map((h, i) => (
|
|
185
|
+
<motion.div
|
|
186
|
+
key={i}
|
|
187
|
+
animate={{ height: [h * 2, h * 5, h * 2] }}
|
|
188
|
+
transition={{ duration: 0.4 + i * 0.1, repeat: Infinity }}
|
|
189
|
+
className="w-0.5 bg-white rounded-full"
|
|
190
|
+
/>
|
|
191
|
+
))}
|
|
192
|
+
</div>
|
|
193
|
+
</div>
|
|
194
|
+
</div>
|
|
195
|
+
</motion.div>
|
|
196
|
+
{/* Control Bar */}
|
|
197
|
+
<motion.div
|
|
198
|
+
transition={{ delay: 0.5, type: "spring", stiffness: 200, damping: 20 }}
|
|
199
|
+
className="flex items-center justify-center gap-2 bg-[#2C2C2E]/90 backdrop-blur-xl px-4 py-1 rounded-full border border-white/5 shadow-2xl z-10"
|
|
200
|
+
>
|
|
201
|
+
<div className="flex items-center gap-1 pr-2 border-r border-white/10">
|
|
202
|
+
<motion.button
|
|
203
|
+
whileHover={{ scale: 1.1 }}
|
|
204
|
+
whileTap={{ scale: 0.95 }}
|
|
205
|
+
onClick={() => setIsMicOn(!isMicOn)}
|
|
206
|
+
className={cn("p-3 rounded-full transition-colors", isMicOn ? "hover:bg-white/10 text-white" : "bg-red-500/20 text-red-500 hover:bg-red-500/30")}
|
|
207
|
+
>
|
|
208
|
+
{isMicOn ? <Mic className="w-5 h-5" /> : <MicOff className="w-5 h-5" />}
|
|
209
|
+
</motion.button>
|
|
210
|
+
<button className="p-1 hover:bg-white/10 rounded-full text-gray-400">
|
|
211
|
+
<ChevronDown className="w-3 h-3" />
|
|
212
|
+
</button>
|
|
213
|
+
</div>
|
|
214
|
+
|
|
215
|
+
<div className="flex items-center gap-1 pr-2 border-white/10">
|
|
216
|
+
<motion.button
|
|
217
|
+
whileHover={{ scale: 1.1 }}
|
|
218
|
+
whileTap={{ scale: 0.95 }}
|
|
219
|
+
onClick={() => setIsVideoOn(!isVideoOn)}
|
|
220
|
+
className={cn("p-3 rounded-full transition-colors", isVideoOn ? "hover:bg-white/10 text-white" : "bg-red-500/20 text-red-500 hover:bg-red-500/30")}
|
|
221
|
+
>
|
|
222
|
+
{isVideoOn ? <Video className="w-5 h-5" /> : <VideoOff className="w-5 h-5" />}
|
|
223
|
+
</motion.button>
|
|
224
|
+
<button className="p-1 hover:bg-white/10 rounded-full text-gray-400">
|
|
225
|
+
<ChevronDown className="w-3 h-3" />
|
|
226
|
+
</button>
|
|
227
|
+
</div>
|
|
228
|
+
<motion.button whileHover={{ scale: 1.1 }} whileTap={{ scale: 0.95 }} className="ml-2 p-3 bg-red-500 hover:bg-red-600 text-white rounded-full transition-colors">
|
|
229
|
+
<PhoneOff className="w-5 h-5" />
|
|
230
|
+
</motion.button>
|
|
231
|
+
<motion.button whileHover={{ scale: 1.1 }} whileTap={{ scale: 0.95 }} className="p-3 hover:bg-white/10 rounded-full text-white transition-colors">
|
|
232
|
+
<Smile className="w-5 h-5" />
|
|
233
|
+
</motion.button>
|
|
234
|
+
|
|
235
|
+
<motion.button whileHover={{ scale: 1.1 }} whileTap={{ scale: 0.95 }} className="p-3 hover:bg-white/10 rounded-full text-white transition-colors">
|
|
236
|
+
<Hand className="w-5 h-5" />
|
|
237
|
+
</motion.button>
|
|
238
|
+
|
|
239
|
+
<motion.button whileHover={{ scale: 1.1 }} whileTap={{ scale: 0.95 }} className="p-3 hover:bg-white/10 rounded-full text-white transition-colors">
|
|
240
|
+
<MoreVertical className="w-5 h-5" />
|
|
241
|
+
</motion.button>
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
</motion.div>
|
|
245
|
+
</div>
|
|
246
|
+
|
|
247
|
+
{/* Right Sidebar */}
|
|
248
|
+
<motion.div
|
|
249
|
+
initial={{ x: 100, opacity: 0 }}
|
|
250
|
+
animate={{ x: 0, opacity: 1 }}
|
|
251
|
+
transition={{ delay: 0.2 }}
|
|
252
|
+
className="w-full lg:w-[380px] h-[350px] lg:h-auto bg-[#F2F2F7] p-4 flex flex-col gap-4 text-black border-t lg:border-t-0 lg:border-l border-white/10 shrink-0"
|
|
253
|
+
>
|
|
254
|
+
|
|
255
|
+
{/* Participants / Chat Card */}
|
|
256
|
+
<motion.div variants={itemVariants} className="bg-white rounded-2xl p-5 shadow-sm flex-1 flex flex-col overflow-hidden">
|
|
257
|
+
{/* Tabs */}
|
|
258
|
+
<div className="flex bg-gray-100 p-1 rounded-xl mb-4">
|
|
259
|
+
<button
|
|
260
|
+
onClick={() => setActiveTab('participants')}
|
|
261
|
+
className={cn("flex-1 py-1.5 text-sm font-medium rounded-lg transition-all", activeTab === 'participants' ? "bg-white shadow-sm text-black" : "text-gray-500 hover:text-gray-700")}
|
|
262
|
+
>
|
|
263
|
+
Participants (5)
|
|
264
|
+
</button>
|
|
265
|
+
<button
|
|
266
|
+
onClick={() => setActiveTab('chat')}
|
|
267
|
+
className={cn("flex-1 py-1.5 text-sm font-medium rounded-lg transition-all flex items-center justify-center gap-2", activeTab === 'chat' ? "bg-white shadow-sm text-black" : "text-gray-500 hover:text-gray-700")}
|
|
268
|
+
>
|
|
269
|
+
Chat
|
|
270
|
+
<span className="bg-[#8B5CF6] text-white text-[10px] px-1.5 py-0.5 rounded-full">2</span>
|
|
271
|
+
</button>
|
|
272
|
+
</div>
|
|
273
|
+
|
|
274
|
+
{/* List */}
|
|
275
|
+
<div className="flex-1 overflow-y-auto pr-2">
|
|
276
|
+
<AnimatePresence mode="wait">
|
|
277
|
+
{activeTab === 'participants' ? (
|
|
278
|
+
<motion.div
|
|
279
|
+
key="participants"
|
|
280
|
+
initial={{ opacity: 0, x: -20 }}
|
|
281
|
+
animate={{ opacity: 1, x: 0 }}
|
|
282
|
+
exit={{ opacity: 0, x: 20 }}
|
|
283
|
+
transition={{ duration: 0.2 }}
|
|
284
|
+
className="space-y-4"
|
|
285
|
+
>
|
|
286
|
+
{participants.map((participant, index) => (
|
|
287
|
+
<motion.div
|
|
288
|
+
key={participant.id}
|
|
289
|
+
initial={{ opacity: 0, y: 10 }}
|
|
290
|
+
animate={{ opacity: 1, y: 0 }}
|
|
291
|
+
transition={{ delay: index * 0.05 }}
|
|
292
|
+
className="flex items-center justify-between group"
|
|
293
|
+
>
|
|
294
|
+
<div className="flex items-center gap-3">
|
|
295
|
+
<Avatar className="w-10 h-10 border border-gray-100">
|
|
296
|
+
<AvatarImage src={participant.image} alt={participant.name} className="object-cover" />
|
|
297
|
+
<AvatarFallback>{participant.name.substring(0, 2)}</AvatarFallback>
|
|
298
|
+
</Avatar>
|
|
299
|
+
<div>
|
|
300
|
+
<p className="text-sm font-semibold text-gray-900">{participant.name}</p>
|
|
301
|
+
<p className="text-xs text-gray-500">{participant.email}</p>
|
|
302
|
+
</div>
|
|
303
|
+
</div>
|
|
304
|
+
|
|
305
|
+
<div className="flex items-center gap-2">
|
|
306
|
+
<button className={cn("p-1.5 rounded-full", participant.isMuted ? "text-gray-400" : "text-[#8B5CF6]")}>
|
|
307
|
+
{participant.isMuted ? <MicOff className="w-4 h-4" /> : <Mic className="w-4 h-4" />}
|
|
308
|
+
</button>
|
|
309
|
+
<button className={cn("p-1.5 rounded-full", participant.isVideoOn ? "text-[#8B5CF6]" : "text-gray-400")}>
|
|
310
|
+
{participant.isVideoOn ? <Video className="w-4 h-4" /> : <VideoOff className="w-4 h-4" />}
|
|
311
|
+
</button>
|
|
312
|
+
</div>
|
|
313
|
+
</motion.div>
|
|
314
|
+
))}
|
|
315
|
+
</motion.div>
|
|
316
|
+
) : (
|
|
317
|
+
<motion.div
|
|
318
|
+
key="chat"
|
|
319
|
+
initial={{ opacity: 0, x: 20 }}
|
|
320
|
+
animate={{ opacity: 1, x: 0 }}
|
|
321
|
+
exit={{ opacity: 0, x: -20 }}
|
|
322
|
+
transition={{ duration: 0.2 }}
|
|
323
|
+
className="h-full flex items-center justify-center text-gray-400 text-sm"
|
|
324
|
+
>
|
|
325
|
+
<p>Chat messages would go here...</p>
|
|
326
|
+
</motion.div>
|
|
327
|
+
)}
|
|
328
|
+
</AnimatePresence>
|
|
329
|
+
</div>
|
|
330
|
+
</motion.div>
|
|
331
|
+
</motion.div>
|
|
332
|
+
</motion.div>
|
|
333
|
+
);
|
|
334
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import React from 'react'
|
|
4
|
+
import VideoConferencePage from './VideoConferencePage'
|
|
5
|
+
|
|
6
|
+
export const VideoConference = {
|
|
7
|
+
name: "Video Conference",
|
|
8
|
+
code: "npx nora-ui add video-conference",
|
|
9
|
+
preview: (
|
|
10
|
+
<VideoConferencePage />
|
|
11
|
+
)
|
|
12
|
+
}
|