modula-ui 1.0.2 → 1.0.4

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 CHANGED
@@ -1,35 +1,98 @@
1
- This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
1
+ # Modula UI
2
2
 
3
- ## Getting Started
4
- First, run the development server:
3
+ A library of modern UI patterns and complete interfaces built with [Shadcn](https://ui.shadcn.com/) and [Tailwind CSS](https://tailwindcss.com/). Get beautiful, functional UIs without building from scratch.
4
+
5
+ ## ✨ Features
6
+
7
+ - 🎨 **Complete UI Patterns** - Full-featured interfaces, not just individual components
8
+ - 🚀 **Easy Integration** - Simple CLI to add entire UI blocks to your project
9
+ - 📦 **Copy & Paste** - Browse interfaces in your browser and copy code directly
10
+ - 🎯 **Modern Stack** - Built with Next.js, React 19, and Tailwind CSS 4
11
+ - 🔧 **Customizable** - Full control over styling and behavior
12
+ - 📱 **Responsive** - Mobile-first design approach
13
+
14
+ ## 🚀 Quick Start
15
+
16
+ ### Browse UI Patterns
17
+
18
+ Run the library locally to browse all available UI patterns and interfaces:
19
+
20
+ ```bash
21
+ npx modula-ui
22
+ ```
23
+
24
+ This will start a local server at `http://localhost:3177` where you can:
25
+ - Preview all UI patterns
26
+ - Copy interface code
27
+ - See live examples
28
+
29
+ ### Add UI Patterns to Your Project
30
+
31
+ Use the CLI to add complete interfaces directly to your project:
32
+
33
+ ```bash
34
+ npx modula-ui add <component-name>
35
+ ```
36
+
37
+ The CLI will automatically:
38
+ 1. Install required dependencies
39
+ 2. Install necessary Shadcn UI components
40
+ 3. Copy the complete interface files to your project
41
+
42
+ ## 📦 Available UI Patterns
43
+
44
+ - **video-conference** - Full-featured video conferencing interface with participant grid, controls, and animations
45
+ - **group-chat** - Modern group chat interface with sidebar, message threads, and real-time feel
46
+
47
+
48
+ ## 🛠️ Installation
49
+
50
+ ### Prerequisites
51
+
52
+ - Node.js >= 18.17.0
53
+ - A Next.js or React project with Tailwind CSS configured
54
+
55
+ ### Example: Adding a UI Block
5
56
 
6
57
  ```bash
7
- npm run dev
8
- # or
9
- yarn dev
10
- # or
11
- pnpm dev
12
- # or
13
- bun dev
58
+ # Add the video conference page
59
+ npx modula-ui add video-conference
60
+
61
+ # Add the group chat interface
62
+ npx modula-ui add group-chat
14
63
  ```
15
64
 
16
- Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
65
+ The interface will be added to `src/library/<type>/` (or `library/<type>/` if no `src` folder exists).
17
66
 
18
- You can start editing the page by modifying `app/page.js`. The page auto-updates as you edit the file.
67
+ ## 🎯 Usage
19
68
 
20
- This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
69
+ After adding a UI pattern, import and use it in your project:
70
+
71
+ ```jsx
72
+ import VideoConferencePage from '@/library/pages/VideoConferencePage';
73
+
74
+ export default function MyPage() {
75
+ return <VideoConferencePage />;
76
+ }
77
+ ```
21
78
 
22
- ## Learn More
79
+ ## 🏗️ Tech Stack
23
80
 
24
- To learn more about Next.js, take a look at the following resources:
81
+ - **Framework:** Next.js 16
82
+ - **UI Library:** React 19
83
+ - **Styling:** Tailwind CSS 4
84
+ - **Components:** Radix UI primitives
85
+ - **Animations:** Framer Motion
86
+ - **Icons:** Lucide React
87
+ - **Charts:** Recharts
25
88
 
26
- - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
27
- - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
89
+ ## 📄 License
28
90
 
29
- You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
91
+ MIT © Mary Ojo
30
92
 
31
- ## Deploy on Vercel
32
93
 
33
- The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
94
+ ## 🔗 Links
34
95
 
35
- Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
96
+ - [npm Package](https://www.npmjs.com/package/modula-ui)
97
+ - [Shadcn UI](https://ui.shadcn.com/)
98
+ - [Tailwind CSS](https://tailwindcss.com/)
package/bin/run.js CHANGED
@@ -125,26 +125,28 @@ if (command === 'add') {
125
125
  process.exit(1);
126
126
  }
127
127
 
128
- } else {
129
- // Default behavior: Start the Next.js server
130
- const port = 3177;
131
- const dev = false;
132
- const app = next({ dev, dir: packageRoot });
133
- const handle = app.getRequestHandler();
134
-
135
- await app.prepare();
136
-
137
- createServer((req, res) => {
138
- const parsedUrl = parse(req.url, true);
139
- handle(req, res, parsedUrl);
140
- }).listen(port, () => {
141
- console.log(`
142
- Your UI Library is LIVE!
143
- Open → http://localhost:${port}
128
+ }
129
+
130
+ // else {
131
+ // // Default behavior: Start the Next.js server
132
+ // const port = 3177;
133
+ // const dev = false;
134
+ // const app = next({ dev, dir: packageRoot });
135
+ // const handle = app.getRequestHandler();
136
+
137
+ // await app.prepare();
138
+
139
+ // createServer((req, res) => {
140
+ // const parsedUrl = parse(req.url, true);
141
+ // handle(req, res, parsedUrl);
142
+ // }).listen(port, () => {
143
+ // console.log(`
144
+ // Your UI Library is LIVE!
145
+ // Open → http://localhost:${port}
144
146
 
145
- Click any component → Copy code → Paste into your project!
147
+ // Click any component → Copy code → Paste into your project!
146
148
 
147
- Press Ctrl+C to stop
148
- `);
149
- });
150
- }
149
+ // Press Ctrl+C to stop
150
+ // `);
151
+ // });
152
+ // }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "modula-ui",
3
- "version": "1.0.2",
4
- "description": "A library of modern interfaces and UI patterns with great UX built with Shadcn",
3
+ "version": "1.0.4",
4
+ "description": "A library of beautiful modern UI blocks and patterns built with Shadcn by Mary Ojo",
5
5
  "keywords": [
6
6
  "tailwind",
7
7
  "components",
@@ -28,15 +28,16 @@
28
28
  "registry.json"
29
29
  ],
30
30
  "scripts": {
31
- "dev": "next dev -p 3177",
31
+ "dev": "next dev",
32
32
  "build": "next build",
33
- "start": "next start -p 3177"
33
+ "start": "next start"
34
34
  },
35
35
  "dependencies": {
36
36
  "@radix-ui/react-avatar": "^1.1.11",
37
37
  "@radix-ui/react-checkbox": "^1.3.3",
38
38
  "@radix-ui/react-dialog": "^1.1.15",
39
39
  "@radix-ui/react-dropdown-menu": "^2.1.16",
40
+ "@radix-ui/react-label": "^2.1.8",
40
41
  "@radix-ui/react-navigation-menu": "^1.2.14",
41
42
  "@radix-ui/react-popover": "^1.1.15",
42
43
  "@radix-ui/react-progress": "^1.1.8",
@@ -46,12 +47,14 @@
46
47
  "@radix-ui/react-slider": "^1.3.6",
47
48
  "@radix-ui/react-slot": "^1.2.4",
48
49
  "@radix-ui/react-tooltip": "^1.2.8",
50
+ "@tsparticles/react": "^3.0.0",
51
+ "@tsparticles/slim": "^3.9.1",
49
52
  "class-variance-authority": "^0.7.1",
50
53
  "clsx": "^2.1.1",
51
54
  "date-fns": "^4.1.0",
52
55
  "framer-motion": "^12.23.24",
53
56
  "lucide-react": "^0.553.0",
54
- "next": "16.0.7",
57
+ "next": "16.0.10",
55
58
  "react": "19.2.0",
56
59
  "react-day-picker": "^9.11.2",
57
60
  "react-dom": "19.2.0",
@@ -0,0 +1,2 @@
1
+ <?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
2
+ <svg width="800px" height="800px" viewBox="-3 0 262 262" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid"><path d="M255.878 133.451c0-10.734-.871-18.567-2.756-26.69H130.55v48.448h71.947c-1.45 12.04-9.283 30.172-26.69 42.356l-.244 1.622 38.755 30.023 2.685.268c24.659-22.774 38.875-56.282 38.875-96.027" fill="#4285F4"/><path d="M130.55 261.1c35.248 0 64.839-11.605 86.453-31.622l-41.196-31.913c-11.024 7.688-25.82 13.055-45.257 13.055-34.523 0-63.824-22.773-74.269-54.25l-1.531.13-40.298 31.187-.527 1.465C35.393 231.798 79.49 261.1 130.55 261.1" fill="#34A853"/><path d="M56.281 156.37c-2.756-8.123-4.351-16.827-4.351-25.82 0-8.994 1.595-17.697 4.206-25.82l-.073-1.73L15.26 71.312l-1.335.635C5.077 89.644 0 109.517 0 130.55s5.077 40.905 13.925 58.602l42.356-32.782" fill="#FBBC05"/><path d="M130.55 50.479c24.514 0 41.05 10.589 50.479 19.438l36.844-35.974C195.245 12.91 165.798 0 130.55 0 79.49 0 35.393 29.301 13.925 71.947l42.211 32.783c10.59-31.477 39.891-54.251 74.414-54.251" fill="#EB4335"/></svg>
Binary file
package/registry.json CHANGED
@@ -13,25 +13,23 @@
13
13
  "avatar"
14
14
  ]
15
15
  },
16
- "group-chat": {
17
- "name": "GroupChat",
18
- "type": "pages",
19
- "path": "src/library/pages/GroupChat/GroupChat.jsx",
16
+ "login-page-01": {
17
+ "name": "Login Page 01",
18
+ "type": "blocks",
19
+ "path": "src/components/login-template/page.jsx",
20
20
  "files": [
21
- "src/library/pages/GroupChat/GroupChat.jsx",
22
- "src/library/pages/GroupChat/data.js"
21
+ "src/components/login-template/page.jsx",
22
+ "src/components/login-template/particles-background.jsx"
23
23
  ],
24
24
  "dependencies": [
25
- "lucide-react"
25
+ "@tsparticles/react",
26
+ "@tsparticles/slim"
26
27
  ],
27
28
  "registryDependencies": [
28
- "sidebar",
29
- "avatar",
30
- "badge",
31
- "scroll-area",
32
- "navigation-menu",
29
+ "button",
30
+ "card",
33
31
  "input",
34
- "button"
32
+ "label"
35
33
  ]
36
34
  }
37
35
  }
@@ -1,12 +1,13 @@
1
1
  @import "tailwindcss";
2
2
  @import "tw-animate-css";
3
3
 
4
+
4
5
  @custom-variant dark (&:is(.dark *));
5
6
 
6
7
  @theme inline {
7
8
  --color-background: var(--background);
8
9
  --color-foreground: var(--foreground);
9
- --font-sans: var(--font-lexend);
10
+ --font-sans: var(--font-dm-sans);
10
11
  --font-mono: var(--font-geist-mono);
11
12
  --color-sidebar-ring: var(--sidebar-ring);
12
13
  --color-sidebar-border: var(--sidebar-border);
@@ -124,3 +125,41 @@
124
125
  font-family: var(--font-lexend), sans-serif;
125
126
  }
126
127
  }
128
+
129
+ @layer components {
130
+ .glass {
131
+ /* Semi-transparent background – usually white or light tint */
132
+ background: rgba(255, 255, 255, 0.15); /* 0.1 – 0.25 is most common */
133
+
134
+ /* The magic line – blurs what's behind */
135
+ backdrop-filter: blur(12px);
136
+ -webkit-backdrop-filter: blur(12px); /* Safari support */
137
+
138
+ /* Softer rounded corners */
139
+ border-radius: 16px;
140
+
141
+ /* Subtle border that looks like glass edge */
142
+ border: 1px solid rgba(255, 255, 255, 0.25);
143
+
144
+ /* Optional – adds depth & modern feel */
145
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.15);
146
+
147
+ /* Important for readability */
148
+ color: white;
149
+ padding: 2rem;
150
+ }
151
+
152
+ .glass-dark {
153
+ background: rgba(31, 31, 32, 0.75); /* dark tint */
154
+ backdrop-filter: blur(25px);
155
+ -webkit-backdrop-filter: blur(25px);
156
+ border: 1px solid rgba(255, 255, 255, 0.12);
157
+ border-radius: 20px;
158
+ box-shadow: 0 10px 40px rgba(0, 0, 0, 0.4);
159
+ color: #f0f0ff;
160
+ }
161
+
162
+ #tsparticles, #tsparticles canvas {
163
+ pointer-events: none !important;
164
+ }
165
+ }
@@ -0,0 +1,6 @@
1
+
2
+ import GroupChat from "@/library/pages/GroupChat/GroupChat"
3
+
4
+ export default function Page() {
5
+ return <GroupChat />
6
+ }
package/src/app/layout.js CHANGED
@@ -1,8 +1,13 @@
1
- import { Lexend, Geist_Mono } from "next/font/google";
1
+ import { DM_Sans, Geist, Geist_Mono } from "next/font/google";
2
2
  import "./globals.css";
3
3
 
4
- const lexend = Lexend({
5
- variable: "--font-lexend",
4
+ const dmSans = DM_Sans({
5
+ variable: "--font-dm-sans",
6
+ subsets: ["latin"],
7
+ });
8
+
9
+ const geistSans = Geist({
10
+ variable: "--font-geist-sans",
6
11
  subsets: ["latin"],
7
12
  });
8
13
 
@@ -13,14 +18,14 @@ const geistMono = Geist_Mono({
13
18
 
14
19
  export const metadata = {
15
20
  title: "Modula UI",
16
- description: "A library of modern interfaces and UI patterns with great UX built with Shadcn",
21
+ description: "A library of beautiful modern UI blocks and patterns built with care and Shadcn by Mary Ojo",
17
22
  };
18
23
 
19
24
  export default function RootLayout({ children }) {
20
25
  return (
21
26
  <html lang="en">
22
27
  <body
23
- className={`${lexend.variable} ${geistMono.variable} antialiased`}
28
+ className={`${dmSans.variable} ${geistSans.variable} ${geistMono.variable} antialiased`}
24
29
  >
25
30
  {children}
26
31
  </body>
@@ -0,0 +1,5 @@
1
+ import LoginPage from "@/components/login-template/page"
2
+
3
+ export default function LoginDemoPage() {
4
+ return <LoginPage />
5
+ }
package/src/app/page.js CHANGED
@@ -34,7 +34,7 @@ export default function Home() {
34
34
  />
35
35
 
36
36
  <main className="space-y-6 w-full">
37
- <PreviewCard>{current?.preview}</PreviewCard>
37
+ <PreviewCard url={current?.url}>{current?.preview}</PreviewCard>
38
38
 
39
39
  <CodeCard code={current?.code} />
40
40
 
@@ -0,0 +1,6 @@
1
+
2
+ import VideoConferencePage from "@/library/pages/VideoConference/VideoConferencePage"
3
+
4
+ export default function Page() {
5
+ return <VideoConferencePage />
6
+ }
@@ -9,7 +9,7 @@ export default function Header({ sidebarOpen, toggleSidebar }) {
9
9
  <div className="mb-8 flex items-center justify-between">
10
10
  <div>
11
11
  <Logo />
12
- <p className="text-gray-600">A library of modern interfaces and UI patterns with great UX built with Shadcn</p>
12
+ <p className="text-gray-600">A library of beautiful modern UI blocks and patterns built with care and Shadcn by <a href="https://www.linkedin.com/in/mary-ojo/" className='underline'>Mary (a Design Engineer)</a></p>
13
13
  </div>
14
14
 
15
15
  <button
@@ -30,7 +30,7 @@ const Logo = ({ className = '' }) => {
30
30
  fill="#111827"
31
31
  letterSpacing="-1"
32
32
  >
33
- modula ui
33
+ Modula UI
34
34
  </text>
35
35
 
36
36
  {/* Gradients */}
@@ -1,80 +1,20 @@
1
- import { useState, useEffect, useRef } from "react";
2
- import { createPortal } from "react-dom";
3
-
4
- export default function PreviewCard({ children, title = "Preview" }) {
5
- const [externalWindow, setExternalWindow] = useState(null);
6
- const [containerElement, setContainerElement] = useState(null);
7
-
1
+ export default function PreviewCard({ children, title = "Preview", url }) {
8
2
  const openFullPreview = () => {
9
- if (externalWindow) {
10
- externalWindow.focus();
11
- return;
3
+ if (url) {
4
+ window.open(url, "_blank");
12
5
  }
13
-
14
- const newWindow = window.open("", "_blank", "width=1200,height=800,left=200,top=200");
15
- if (!newWindow) return;
16
-
17
- const fullPage = `
18
- <!DOCTYPE html>
19
- <html lang="en">
20
- <head>
21
- <meta charset="utf-8">
22
- <meta name="viewport" content="width=device-width, initial-scale=1">
23
- <title>${title}</title>
24
- <script src="https://cdn.tailwindcss.com"></script>
25
- <style>
26
- body { margin: 0; background: #f9fafb; }
27
- @media (prefers-color-scheme: dark) { body { background: #111827; } }
28
- </style>
29
- </head>
30
- <body class="min-h-screen flex items-center justify-center">
31
- <div id="root" class="w-full mx-auto"></div>
32
- </body>
33
- </html>
34
- `;
35
-
36
- newWindow.document.write(fullPage);
37
- newWindow.document.close();
38
-
39
- // Wait for the window to load before trying to access the DOM
40
- newWindow.onload = () => {
41
- const container = newWindow.document.getElementById("root");
42
- setExternalWindow(newWindow);
43
- setContainerElement(container);
44
- };
45
-
46
- // In case onload fired synchronously or we missed it (rare with open, but good safety)
47
- if (newWindow.document.readyState === 'complete') {
48
- const container = newWindow.document.getElementById("root");
49
- setExternalWindow(newWindow);
50
- setContainerElement(container);
51
- }
52
-
53
- // Handle window close
54
- newWindow.onbeforeunload = () => {
55
- setExternalWindow(null);
56
- setContainerElement(null);
57
- };
58
6
  };
59
7
 
60
- // Close external window when component unmounts
61
- useEffect(() => {
62
- return () => {
63
- if (externalWindow) {
64
- externalWindow.close();
65
- }
66
- };
67
- }, [externalWindow]);
68
-
69
8
  return (
70
9
  <div className="w-full bg-white rounded-lg shadow-sm border border-gray-200 p-3">
71
10
  <div className="flex items-center justify-between mb-4">
72
11
  <h2 className="text-xl font-semibold text-gray-900">{title}</h2>
73
12
 
74
13
  <button
14
+ type="button"
75
15
  onClick={openFullPreview}
76
16
  className={`flex items-center gap-1.5 px-3 py-1.5 text-sm font-medium rounded-md transition
77
- text-gray-700 bg-gray-50 hover:bg-gray-100 hover:text-gray-900`}
17
+ text-gray-700 bg-gray-50 hover:bg-gray-100 hover:text-gray-900 cursor-pointer`}
78
18
  >
79
19
  <svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
80
20
  <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" />
@@ -86,13 +26,10 @@ export default function PreviewCard({ children, title = "Preview" }) {
86
26
  {/* The preview area in the main window */}
87
27
  <div
88
28
  id="preview-content"
89
- className="bg-gray-50 rounded-lg p-4 border border-dashed border-gray-300 min-h-96 w-full"
29
+ className="bg-gray-50 rounded-lg p-4 border border-dashed border-gray-300 min-h-96 max-h-[600px] w-full overflow-hidden"
90
30
  >
91
31
  {children}
92
32
  </div>
93
-
94
- {/* Render content into the new window if it exists */}
95
- {containerElement && createPortal(children, containerElement)}
96
33
  </div>
97
34
  );
98
35
  }
@@ -15,7 +15,7 @@ export default function Sidebar({ selected, onSelect, open, onClose }) {
15
15
  style={{ top: '1.5rem' }} // matches Header padding
16
16
  >
17
17
  <div className="flex items-center justify-between mb-4 lg:mb-3">
18
- <h2 className="font-semibold text-gray-700">Patterns</h2>
18
+ <h2 className="font-semibold text-gray-700">Blocks</h2>
19
19
  <button
20
20
  onClick={onClose}
21
21
  className="lg:hidden p-1 hover:bg-gray-100 rounded"
@@ -0,0 +1,2 @@
1
+ <?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
2
+ <svg width="800px" height="800px" viewBox="-3 0 262 262" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid"><path d="M255.878 133.451c0-10.734-.871-18.567-2.756-26.69H130.55v48.448h71.947c-1.45 12.04-9.283 30.172-26.69 42.356l-.244 1.622 38.755 30.023 2.685.268c24.659-22.774 38.875-56.282 38.875-96.027" fill="#4285F4"/><path d="M130.55 261.1c35.248 0 64.839-11.605 86.453-31.622l-41.196-31.913c-11.024 7.688-25.82 13.055-45.257 13.055-34.523 0-63.824-22.773-74.269-54.25l-1.531.13-40.298 31.187-.527 1.465C35.393 231.798 79.49 261.1 130.55 261.1" fill="#34A853"/><path d="M56.281 156.37c-2.756-8.123-4.351-16.827-4.351-25.82 0-8.994 1.595-17.697 4.206-25.82l-.073-1.73L15.26 71.312l-1.335.635C5.077 89.644 0 109.517 0 130.55s5.077 40.905 13.925 58.602l42.356-32.782" fill="#FBBC05"/><path d="M130.55 50.479c24.514 0 41.05 10.589 50.479 19.438l36.844-35.974C195.245 12.91 165.798 0 130.55 0 79.49 0 35.393 29.301 13.925 71.947l42.211 32.783c10.59-31.477 39.891-54.251 74.414-54.251" fill="#EB4335"/></svg>
@@ -0,0 +1,4 @@
1
+ #tsparticles,
2
+ #tsparticles canvas {
3
+ pointer-events: none !important;
4
+ }
@@ -0,0 +1,9 @@
1
+
2
+ import LoginPage from "./page"
3
+
4
+ export const LoginDemo = {
5
+ name: "Login Page 01",
6
+ code: "npx modula-ui add login-page-01",
7
+ preview: <LoginPage />,
8
+ url: "/login-demo"
9
+ }
@@ -0,0 +1,99 @@
1
+ "use client"
2
+
3
+ import { Button } from "@/components/ui/button"
4
+ import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card"
5
+ import { Input } from "@/components/ui/input"
6
+ import { Label } from "@/components/ui/label"
7
+ import { ParticlesBackground } from "./particles-background"
8
+ import logo from "../../../public/logo_cloudsync.png"
9
+ import google from "../../../public/google.svg"
10
+ import Image from "next/image"
11
+
12
+ export default function LoginPage() {
13
+ return (
14
+ <div className="dark font-sans w-full min-h-screen flex flex-col lg:flex-row items-center justify-center lg:justify-end overflow-hidden bg-background text-foreground">
15
+ <ParticlesBackground />
16
+
17
+ <div className="w-full z-10 px-10 sm:px-6 lg:px-20 py-12 md:py-12 flex flex-col lg:flex-row items-center justify-center gap-8 lg:gap-28">
18
+ <div className="hidden lg:block space-y-4 w-full lg:w-1/2">
19
+ <div className="flex items-center gap-4 mb-4">
20
+ <Image
21
+ src={logo}
22
+ alt="Logo"
23
+ width={65}
24
+ height={65}
25
+ />
26
+ </div>
27
+ <h3 className="text-2xl sm:text-3xl md:text-4xl lg:text-5xl font-bold tracking-tight text-white">Secure, Scalable and Seamless</h3>
28
+ <p className="text-sm sm:text-base md:text-lg text-muted-foreground">Empowering businesses with world-class cloud computing infrastructure and 99.9% uptime. Managed, monitored and optimized for your growth.</p>
29
+ </div>
30
+ <div className="lg:hidden flex justify-center mb-4">
31
+ <Image
32
+ src={logo}
33
+ alt="Logo"
34
+ width={65}
35
+ height={65}
36
+ />
37
+ </div>
38
+ <Card className="w-full md:w-[70%] lg:w-1/2 glass-dark">
39
+ <CardHeader className="space-y-1 ">
40
+ <CardTitle className="text-xl sm:text-2xl font-bold tracking-tight">Welcome back</CardTitle>
41
+ <CardDescription className="text-sm sm:text-base text-muted-foreground/80">
42
+ Enter your email to sign in to your account
43
+ </CardDescription>
44
+ </CardHeader>
45
+ <div className="relative my-1">
46
+ <div className="absolute inset-0 flex items-center">
47
+ <span className="w-full border-t border-muted-foreground/20" />
48
+ </div>
49
+ </div>
50
+ <CardContent className="space-y-4">
51
+ <div className="space-y-2">
52
+ <Label htmlFor="email" className="text-sm">Email</Label>
53
+ <Input id="email" placeholder="m@example.com" required type="email" className="bg-white/5 backdrop-blur-sm border-white/10 focus:bg-white/10 focus:border-white/20 transition-all text-sm" />
54
+ </div>
55
+ <div className="space-y-2">
56
+ <Label htmlFor="password" className="text-sm">Password</Label>
57
+ <Input id="password" required placeholder="********" type="password" className="bg-white/5 backdrop-blur-sm border-white/10 focus:bg-white/10 focus:border-white/20 transition-all text-sm" />
58
+ </div>
59
+ <Button className="w-full bg-cyan-500 shadow-lg shadow-primary/20 hover:shadow-primary/40 transition-shadow text-sm sm:text-base" type="submit">
60
+ Sign In
61
+ </Button>
62
+
63
+ <div className="relative my-2 mb-3">
64
+ <div className="absolute inset-0 flex items-center">
65
+ <span className="w-full border-t border-muted-foreground/20" />
66
+ </div>
67
+ <div className="relative flex justify-center text-xs uppercase">
68
+ <span className="bg-transparent px-2 text-muted-foreground">
69
+ Or continue with
70
+ </span>
71
+ </div>
72
+ </div>
73
+
74
+ <Button variant="outline" className="w-full bg-white/5 backdrop-blur-sm border-white/10 hover:bg-white/10 hover:text-white text-sm sm:text-base" onClick={() => { }}>
75
+ <Image
76
+ src={google}
77
+ alt="Google"
78
+ width={16}
79
+ height={16}
80
+ />
81
+ Google
82
+ </Button>
83
+ </CardContent>
84
+ <CardFooter className="flex flex-col gap-2 text-center text-xs sm:text-sm text-muted-foreground">
85
+ <a href="#" className="hover:text-primary transition-colors hover:underline">
86
+ Forgot your password?
87
+ </a>
88
+ <div className="text-xs">
89
+ Don't have an account?{" "}
90
+ <a href="#" className="font-medium text-primary hover:underline transition-colors">
91
+ Sign up for free
92
+ </a>
93
+ </div>
94
+ </CardFooter>
95
+ </Card>
96
+ </div>
97
+ </div>
98
+ )
99
+ }
@@ -0,0 +1,100 @@
1
+ "use client"
2
+
3
+ import { useEffect, useState } from "react"
4
+ import Particles, { initParticlesEngine } from "@tsparticles/react"
5
+ import { loadSlim } from "@tsparticles/slim"
6
+
7
+ export function ParticlesBackground() {
8
+ const [init, setInit] = useState(false)
9
+
10
+ useEffect(() => {
11
+ initParticlesEngine(async (engine) => {
12
+ await loadSlim(engine)
13
+ }).then(() => {
14
+ setInit(true)
15
+ })
16
+ }, [])
17
+
18
+ if (!init) {
19
+ return null
20
+ }
21
+
22
+ const options = {
23
+ particles: {
24
+ number: {
25
+ value: 30,
26
+ density: {
27
+ enable: true,
28
+ width: 800,
29
+ height: 800
30
+ }
31
+ },
32
+ color: {
33
+ value: "#FFFEE0"
34
+ },
35
+ shape: {
36
+ type: "circle",
37
+ },
38
+ opacity: {
39
+ value: 0.5,
40
+ animation: {
41
+ enable: false
42
+ }
43
+ },
44
+ size: {
45
+ value: { min: 1, max: 3 },
46
+ },
47
+ links: {
48
+ enable: true,
49
+ distance: 150,
50
+ color: "#00FFFF",
51
+ opacity: 0.2,
52
+ width: 1
53
+ },
54
+ move: {
55
+ enable: true,
56
+ speed: 0.1,
57
+ direction: "none",
58
+ random: false,
59
+ straight: false,
60
+ outModes: {
61
+ default: "out"
62
+ },
63
+ }
64
+ },
65
+ interactivity: {
66
+ detectsOn: "canvas",
67
+ events: {
68
+ onHover: {
69
+ enable: false,
70
+ mode: "repulse"
71
+ },
72
+ onClick: {
73
+ enable: false,
74
+ mode: "push"
75
+ },
76
+ resize: {
77
+ enable: false
78
+ }
79
+ },
80
+ modes: {
81
+ repulse: {
82
+ distance: 100,
83
+ duration: 0.4
84
+ },
85
+ push: {
86
+ quantity: 4
87
+ }
88
+ }
89
+ },
90
+ retina_detect: true
91
+ }
92
+
93
+ return (
94
+ <Particles
95
+ id="tsparticles"
96
+ className="pointer-events-none"
97
+ options={options}
98
+ />
99
+ )
100
+ }
@@ -0,0 +1,24 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import * as LabelPrimitive from "@radix-ui/react-label"
5
+
6
+ import { cn } from "@/lib/utils"
7
+
8
+ function Label({
9
+ className,
10
+ ...props
11
+ }) {
12
+ return (
13
+ <LabelPrimitive.Root
14
+ data-slot="label"
15
+ className={cn(
16
+ "flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
17
+ className
18
+ )}
19
+ {...props}
20
+ />
21
+ )
22
+ }
23
+
24
+ export { Label }
@@ -2,11 +2,12 @@ import { VideoConference } from "@/library/pages/VideoConference";
2
2
  import { ReservationsOverview } from "@/library/pages/ReservationsOverview";
3
3
  import { FitnessOverview } from "@/library/pages/FitnessPage";
4
4
  import { GroupChat } from "@/library/pages/GroupChat";
5
-
5
+ import { LoginDemo } from "@/components/login-template";
6
6
 
7
7
  export const components = {
8
8
  VideoConference,
9
- GroupChat,
9
+ // GroupChat,
10
+ LoginDemo,
10
11
  // ReservationsOverview,
11
12
  // FitnessOverview
12
13
  };
@@ -8,5 +8,6 @@ export const GroupChat = {
8
8
  code: "npx modula-ui add group-chat",
9
9
  preview: (
10
10
  <GroupChatCode />
11
- )
11
+ ),
12
+ url: "/group-chat"
12
13
  }
@@ -8,5 +8,6 @@ export const VideoConference = {
8
8
  code: "npx modula-ui add video-conference",
9
9
  preview: (
10
10
  <VideoConferencePage />
11
- )
11
+ ),
12
+ url: "/video-conference"
12
13
  }