Add Webapp

This commit is contained in:
clinton committed 2026-04-03 12:35:13 +02:00
commit ed924ef2ac
76 files changed
+17054

No files matched your search

+19
View File
@@ -0,0 +1,19 @@
import React from 'react'
export default function CallScreen() {
return (
<div className='w-full flex flex-col h-screen'>
<div className='flex justify-evenly'>
<button>Call</button>
<button>White board</button>
<button>Settings</button>
</div>
<div className='grow bg-blue-200 flex'>
<div className='grow'>
<video />
</div>
<div>Chat</div>
</div>
</div>
)
}
+11
View File
@@ -0,0 +1,11 @@
import { ListUsers } from '@/actions/users/UserActions'
import ChatBox from '@/components/chat/ChatBox'
import React from 'react'
export default async function LoggedIn() {
const users = await ListUsers()
return (<>
<div>{JSON.stringify(users)}</div>
<ChatBox />
</>)
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 279 KiB

+138
View File
@@ -0,0 +1,138 @@
@import "tailwindcss";
@import "tw-animate-css";
@import "shadcn/tailwind.css";
@custom-variant dark (&:is(.dark *));
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--font-sans: var(--font-sans);
--font-mono: var(--font-geist-mono);
--font-heading: var(--font-sans);
--color-sidebar-ring: var(--sidebar-ring);
--color-sidebar-border: var(--sidebar-border);
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
--color-sidebar-accent: var(--sidebar-accent);
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
--color-sidebar-primary: var(--sidebar-primary);
--color-sidebar-foreground: var(--sidebar-foreground);
--color-sidebar: var(--sidebar);
--color-chart-5: var(--chart-5);
--color-chart-4: var(--chart-4);
--color-chart-3: var(--chart-3);
--color-chart-2: var(--chart-2);
--color-chart-1: var(--chart-1);
--color-ring: var(--ring);
--color-input: var(--input);
--color-border: var(--border);
--color-destructive: var(--destructive);
--color-accent-foreground: var(--accent-foreground);
--color-accent: var(--accent);
--color-muted-foreground: var(--muted-foreground);
--color-muted: var(--muted);
--color-secondary-foreground: var(--secondary-foreground);
--color-secondary: var(--secondary);
--color-primary-foreground: var(--primary-foreground);
--color-primary: var(--primary);
--color-popover-foreground: var(--popover-foreground);
--color-popover: var(--popover);
--color-card-foreground: var(--card-foreground);
--color-card: var(--card);
--radius-sm: calc(var(--radius) * 0.6);
--radius-md: calc(var(--radius) * 0.8);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) * 1.4);
--radius-2xl: calc(var(--radius) * 1.8);
--radius-3xl: calc(var(--radius) * 2.2);
--radius-4xl: calc(var(--radius) * 2.6);
}
:root {
--background: oklch(1 0 0);
--foreground: oklch(0.145 0 0);
--card: oklch(1 0 0);
--card-foreground: oklch(0.145 0 0);
--popover: oklch(1 0 0);
--popover-foreground: oklch(0.145 0 0);
--primary: oklch(0.205 0 0);
--primary-foreground: oklch(0.985 0 0);
--secondary: oklch(0.97 0 0);
--secondary-foreground: oklch(0.205 0 0);
--muted: oklch(0.97 0 0);
--muted-foreground: oklch(0.556 0 0);
--accent: oklch(0.97 0 0);
--accent-foreground: oklch(0.205 0 0);
--destructive: oklch(0.577 0.245 27.325);
--border: oklch(0.922 0 0);
--input: oklch(0.922 0 0);
--ring: oklch(0.708 0 0);
--chart-1: oklch(0.87 0 0);
--chart-2: oklch(0.556 0 0);
--chart-3: oklch(0.439 0 0);
--chart-4: oklch(0.371 0 0);
--chart-5: oklch(0.269 0 0);
--radius: 0.625rem;
--sidebar: oklch(0.985 0 0);
--sidebar-foreground: oklch(0.145 0 0);
--sidebar-primary: oklch(0.205 0 0);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.97 0 0);
--sidebar-accent-foreground: oklch(0.205 0 0);
--sidebar-border: oklch(0.922 0 0);
--sidebar-ring: oklch(0.708 0 0);
}
.dark {
--background: oklch(0.145 0 0);
--foreground: oklch(0.985 0 0);
--card: oklch(0.205 0 0);
--card-foreground: oklch(0.985 0 0);
--popover: oklch(0.205 0 0);
--popover-foreground: oklch(0.985 0 0);
--primary: oklch(0.922 0 0);
--primary-foreground: oklch(0.205 0 0);
--secondary: oklch(0.269 0 0);
--secondary-foreground: oklch(0.985 0 0);
--muted: oklch(0.269 0 0);
--muted-foreground: oklch(0.708 0 0);
--accent: oklch(0.269 0 0);
--accent-foreground: oklch(0.985 0 0);
--destructive: oklch(0.704 0.191 22.216);
--border: oklch(1 0 0 / 10%);
--input: oklch(1 0 0 / 15%);
--ring: oklch(0.556 0 0);
--chart-1: oklch(0.87 0 0);
--chart-2: oklch(0.556 0 0);
--chart-3: oklch(0.439 0 0);
--chart-4: oklch(0.371 0 0);
--chart-5: oklch(0.269 0 0);
--sidebar: oklch(0.205 0 0);
--sidebar-foreground: oklch(0.985 0 0);
--sidebar-primary: oklch(0.488 0.243 264.376);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.269 0 0);
--sidebar-accent-foreground: oklch(0.985 0 0);
--sidebar-border: oklch(1 0 0 / 10%);
--sidebar-ring: oklch(0.556 0 0);
}
@layer base {
* {
@apply border-border outline-ring/50;
}
body {
@apply bg-background text-foreground;
}
html {
@apply font-sans;
}
}
::view-transition-group(.animate-in){
animation: animate-in ease-in 0.5s;
}
::view-transition-group(.animate-out){
animation: animate-out ease-in 0.5s;
}
+65
View File
@@ -0,0 +1,65 @@
// import React from "react";
// export default function RootLayoutClient({ children }) {
// React.useEffect(() => {
// if ("serviceWorker" in navigator) {
// navigator.serviceWorker
// .register("/service-worker.js")
// .then((registration) => {
// console.log("Service Worker registered with scope:", registration.scope);
// })
// .catch((error) => {
// console.error("Service Worker registration failed:", error);
// });
// }
// }, []);
// return (
// <div className="text-white flex flex-col">
// <div className="container mx-auto px-4 max-w-[1024px]">
// {children}
// </div>
// </div>
// );
// }
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";
import React from "react";
import RootLayoutClient from "./layoutClient";
const geistSans = Geist({
variable: "--font-geist-sans",
subsets: ["latin"],
});
const geistMono = Geist_Mono({
variable: "--font-geist-mono",
subsets: ["latin"],
});
export const metadata: Metadata = {
title: "CoffeeChat",
description: "Start chatting with your communities",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body
className={`${geistSans.variable} ${geistMono.variable} antialiased h-full w-full`}
>
<RootLayoutClient>
{children}
</RootLayoutClient>
</body>
</html>
);
}
+47
View File
@@ -0,0 +1,47 @@
"use client";
import React from "react";
// import React from "react";
// export default function RootLayoutClient({ children }) {
// React.useEffect(() => {
// if ("serviceWorker" in navigator) {
// navigator.serviceWorker
// .register("/service-worker.js")
// .then((registration) => {
// console.log("Service Worker registered with scope:", registration.scope);
// })
// .catch((error) => {
// console.error("Service Worker registration failed:", error);
// });
// }
// }, []);
// return (
// <div className="text-white flex flex-col">
// <div className="container mx-auto px-4 max-w-[1024px]">
// {children}
// </div>
// </div>
// );
// }
export default function RootLayoutClient({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
React.useEffect(() => {
if ("serviceWorker" in navigator) {
navigator.serviceWorker
.register("/service-worker.js")
.then((registration) => {
console.log("Service Worker registered with scope:", registration.scope);
})
.catch((error) => {
console.error("Service Worker registration failed:", error);
});
}
}, []);
return (children);
}
+11
View File
@@ -0,0 +1,11 @@
import { LoginForm } from "@/components/login-form"
export default function Page() {
return (
<div className="flex min-h-svh w-full items-center justify-center p-6 md:p-10">
<div className="w-full max-w-sm">
<LoginForm />
</div>
</div>
)
}
+81
View File
@@ -0,0 +1,81 @@
import type { MetadataRoute } from "next";
export default function manifest(): MetadataRoute.Manifest {
return {
name: "CoffeeChat",
short_name: "CoffeeChat",
start_url: "chat.rootbranch.co.za",
display: "standalone",
description: "Start chatting with your communities",
lang: " en",
dir: "auto",
theme_color: "#59168b",
background_color: "#59168b",
orientation: "any",
icons: [
{
src: "/manifest-icon-192.maskable.png",
sizes: "192x192",
type: "image/png",
purpose: "any",
},
{
src: "/manifest-icon-192.maskable.png",
sizes: "192x192",
type: "image/png",
purpose: "maskable",
},
{
src: "/manifest-icon-512.maskable.png",
sizes: "512x512",
type: "image/png",
purpose: "any",
},
{
src: "/manifest-icon-512.maskable.png",
sizes: "512x512",
type: "image/png",
purpose: "maskable",
},
],
screenshots: [
{
src: "screen1.png",
sizes: "2880x1800",
type: "image/png",
form_factor: "wide",
label: "Communities Home Page",
},
{
src: "screen2.png",
sizes: "570x1265",
type: "image/png",
form_factor: "narrow",
label: "Communities Home Page",
},
],
related_applications: [
{
platform: "windows",
url: "https://chat.rootbranch.co.za",
},
],
prefer_related_applications: false,
shortcuts: [
{
name: "CoffeeChat",
url: "chat.rootbranch.co.za",
description: "Start chatting with your communities",
icons: [
{
src: "manifest-icon-96.maskable.png",
purpose: "any",
sizes: "96x96",
type: "image/png",
},
],
},
],
display_override: ["fullscreen"],
};
}
+56
View File
@@ -0,0 +1,56 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { LoginFormAction } from "@/actions/auth/AuthActions";
import { getRooms } from "@/actions/room/RoomActions";
import RoomCard from "@/components/RoomCard";
import { Handshake } from "lucide-react";
import { cookies } from "next/headers";
import Image from "next/image";
import Link from "next/link";
import { ViewTransition } from "react";
export default async function Home() {
const cj = await cookies()
const session = cj.get(`ccsession`)
const rooms = await getRooms()
return (
<div className="flex flex-col min-h-screen bg-black font-sans">
<form
className="flex flex-col lg:flex-row p-2 w-full bg-purple-900 text-white shadow-sm shadow-purple-400 z-20"
action={LoginFormAction}>
<div className="grow flex">
<Handshake size={30} className="me-2" />
<input className=" w-full focus:p-2 transition-all" placeholder="Find" />
</div>
{/* <input type="email" name="email" placeholder="Email" />
<input type="password" name="password" placeholder="Password" /> */}
{!session &&
<div className="flex">
<Link className="px-2" href={`/login`}>Login</Link>
<ViewTransition name="register-button" exit={'animate-out'}>
<Link className="px-2" href={`/register`}>Register</Link>
</ViewTransition>
</div>
}
</form>
<div className="grid grid-cols-1 lg:grid-cols-3 m-2">
{rooms.map((room: {
title: string;
room: string;
image: any;
color: string;
}, i: number) =>
<RoomCard
key={`Card-${i}`}
color={room.color}
image={room.image}
room={room.room}
title={room.title}
/>
)}
</div>
{/* {JSON.stringify(rooms)} */}
</div>
);
}
+10
View File
@@ -0,0 +1,10 @@
'use client'
import ErrorDialog from '@/components/Dialogs/ErrorDialog'
import React from 'react'
export default function error({ error }: { error: { message: string } }) {
return (
<ErrorDialog error={JSON.parse(error.message).error} />
// <div>error : {JSON.stringify(JSON.parse(error.message).error)}</div>
)
}
+57
View File
@@ -0,0 +1,57 @@
import { RegisterFormAction } from '@/actions/auth/AuthActions'
import Link from 'next/link'
import React, { ViewTransition } from 'react'
// import { ViewTransition } from 'react'
export default function Register() {
return (<ViewTransition enter={'auto'} exit={'auto'} default={'auto'}>
<div className='bg-black flex flex-col'>
<div
className='bg-white/10 h-svh max-w-[100vw] overflow-hidden backdrop-blur-lg flex flex-col border border-white/20 shadow-lg'
>
<div className='bg-purple-950 text-white flex justify-evenly p-2 font-bold'>
<div>Register Your Profile</div>
</div>
<form action={RegisterFormAction} className='m-2 flex flex-col grow'>
<div className='grow'>
<div className='text-white'>
<div className='flex'>
<label className='me-2 w-1/2'>Your name</label>
<input placeholder='Your name' type='text' name='name' className='grow' />
</div>
<small style={{ fontSize: '0.5rem' }} className='text-xs text-justify '>This will be used for your records but aliases are allowed while using chats</small>
</div>
<hr />
<div className='text-white'>
<div className='flex'>
<label className='me-2 w-1/2'>Email</label>
<input placeholder='Email' type='email' name='email' className='grow' />
</div>
<small style={{ fontSize: '0.5rem' }} className='text-xs text-justify '>This will be used to login to the system and gain access to restricted groups</small>
</div>
<hr />
<div className='text-white'>
<div className='flex'>
<label className='me-2 w-1/2'>Password</label>
<input placeholder='Password' type='password' name='password' className='grow' />
</div>
</div>
<hr />
</div>
<div className='flex w-full justify-between p-2'>
<Link href={`/`}><button type='button' className='self-end bg-purple-900 p-2 text-white'>Cancel</button></Link>
<ViewTransition name='register-button' enter={'animate-in'}>
<button className='self-end bg-purple-900 p-2 text-white'>Register</button>
</ViewTransition>
</div>
{/*
</div>
*/}
</form>
</div>
</div>
</ViewTransition>
)
}
+11
View File
@@ -0,0 +1,11 @@
import { SignupForm } from "@/components/signup-form"
export default function Page() {
return (
<div className="flex min-h-svh w-full items-center justify-center p-6 md:p-10">
<div className="w-full max-w-sm">
<SignupForm />
</div>
</div>
)
}
+10
View File
@@ -0,0 +1,10 @@
import MediaSoupWidget from '@/components/chat/MediaSoupWidget'
import WebRTCChat from '@/components/chat/WebRTCChatUsingPeer'
import React from 'react'
export default function Tttest() {
return (
<div><WebRTCChat /></div>
// <div><MediaSoupWidget /></div>
)
}
+16
View File
@@ -0,0 +1,16 @@
import ChatNoServerWidget from '@/components/chat/ChatNoServerWidget'
import ChatWS from '@/components/chat/ChatWS'
import MediaSoupWidget from '@/components/chat/MediaSoupWidget'
import WebRTCChatUsingPeer from '@/components/chat/WebRTCChatUsingPeer'
import WebRTCChat from '@/components/chat/WebRTCChatUsingPeer'
import WebRTComponent from '@/components/chat/WebRTComponent'
import React from 'react'
export default function Tttest() {
return (
// <div><WebRTCChat /></div>
// <div><ChatNoServerWidget /></div>
// <div><WebRTCChatUsingPeer /></div>
<div><ChatWS /></div>
)
}
+33
View File
@@ -0,0 +1,33 @@
import Link from 'next/link'
import React from 'react'
export default function Search() {
return (
<div className='w-screen h-screen'
style={{ 'background': 'conic-gradient(from 210deg, #c6bcb9 0.000deg, #c6bcb9 24.000deg, #b9b6b9 calc(24.000deg + 0.1deg), #b9b6b9 48.000deg, #aab0b8 calc(48.000deg + 0.1deg), #aab0b8 72.000deg, #9ba9b6 calc(72.000deg + 0.1deg), #9ba9b6 96.000deg, #8ca2b4 calc(96.000deg + 0.1deg), #8ca2b4 120.000deg, #7e99b0 calc(120.000deg + 0.1deg), #7e99b0 144.000deg, #7191ac calc(144.000deg + 0.1deg), #7191ac 168.000deg, #6688a7 calc(168.000deg + 0.1deg), #6688a7 192.000deg, #5d7fa2 calc(192.000deg + 0.1deg), #5d7fa2 216.000deg, #56769c calc(216.000deg + 0.1deg), #56769c 240.000deg, #526d95 calc(240.000deg + 0.1deg), #526d95 264.000deg, #50648e calc(264.000deg + 0.1deg), #50648e 288.000deg, #515b86 calc(288.000deg + 0.1deg), #515b86 312.000deg, #55547e calc(312.000deg + 0.1deg), #55547e 336.000deg, #5c4c75 calc(336.000deg + 0.1deg) 360.000deg)' }}
>
<div className='bg-purple-900 flex p-2'>
<input placeholder='Find' className='text-2xl grow bg-white' />
</div>
<div className='grid grid-cols-3'>
<RoomCard title={'Dating'} room={"123"} />
<RoomCard title={'Business'} room={"123"} />
<RoomCard title={'Programming'} room={"123"} />
<RoomCard title={'Somthing Else'} room={"123"} />
</div>
</div>
)
}
function RoomCard({
title,
room,
}: {
title: string
room: string
}) {
return <Link href={`/testb?room=${room}`}><div>{title}</div></Link>
}
+98
View File
@@ -0,0 +1,98 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
'use client'
import RoomCard from '@/components/RoomCard'
import { CallApi } from '@/helper/api/ApiConnector'
import Link from 'next/link'
import React, { useState } from 'react'
export default function Search() {
const [details, setDetails] = useState<{
id?: number;
image: any,
title: string,
room: string,
color: string
}>()
const handleSave = async () => {
console.log(await CallApi(`/room`, 'POST', { id: 0, ...details,image: details?.image }))
}
return (
<div className='w-screen h-screen bg-black'
// style={{ 'background': 'conic-gradient(from 210deg, #c6bcb9 0.000deg, #c6bcb9 24.000deg, #b9b6b9 calc(24.000deg + 0.1deg), #b9b6b9 48.000deg, #aab0b8 calc(48.000deg + 0.1deg), #aab0b8 72.000deg, #9ba9b6 calc(72.000deg + 0.1deg), #9ba9b6 96.000deg, #8ca2b4 calc(96.000deg + 0.1deg), #8ca2b4 120.000deg, #7e99b0 calc(120.000deg + 0.1deg), #7e99b0 144.000deg, #7191ac calc(144.000deg + 0.1deg), #7191ac 168.000deg, #6688a7 calc(168.000deg + 0.1deg), #6688a7 192.000deg, #5d7fa2 calc(192.000deg + 0.1deg), #5d7fa2 216.000deg, #56769c calc(216.000deg + 0.1deg), #56769c 240.000deg, #526d95 calc(240.000deg + 0.1deg), #526d95 264.000deg, #50648e calc(264.000deg + 0.1deg), #50648e 288.000deg, #515b86 calc(288.000deg + 0.1deg), #515b86 312.000deg, #55547e calc(312.000deg + 0.1deg), #55547e 336.000deg, #5c4c75 calc(336.000deg + 0.1deg) 360.000deg)' }}
>
<div className='grow flex flex-col items-center align-middle justify-center h-full w-full'>
<div
className='bg-white/10 w-full h-full lg:h-[90%] lg:w-[90%] m-2 backdrop-blur-lg flex flex-col border border-white/20 shadow-lg'
>
<div className='bg-purple-950 text-white flex justify-evenly'>
<button className='p-2 grow hover:backdrop-blur-lg hover:bg-white/10'>Room Details</button>
<button className='p-2 grow hover:backdrop-blur-lg hover:bg-white/10'>Audiance</button>
<button className='p-2 grow hover:backdrop-blur-lg hover:bg-white/10'>Rules</button>
<button className='p-2 grow hover:backdrop-blur-lg hover:bg-white/10'>Guest List</button>
</div>
<div>
<input placeholder='Room Name'
onChange={e => setDetails((d: any) => {
return { ...d, title: e.target.value }
})}
className='text-white w-full border-0 focus:p-2 transition-all focus:border-0' />
<button onClick={handleSave} className='text-white'>Save</button>
<div>
<label>Image</label>
<input
onChange={e => {
const file = e.target.files![0]
if (file && file.type.startsWith('image/')) {
const reader = new FileReader(); // Create a new FileReader instance
// Set up the onload event for the reader
reader.onload = function (e) {
// e.target.result contains the image data as a Base64 encoded URL (data URL)
console.log('Image Data URL:', e.target!.result);
// You can now set the src of an image tag to this data URL to display it
setDetails((d: any) => {
return { ...d, image: e.target!.result }
})
// imagePreview.src = e.target.result;
// To get pixel data (ImageData object), you would use a canvas element
// (See "Getting Pixel Data" below)
};
// Read the image file as a Data URL
reader.readAsDataURL(file);
} else {
// imagePreview.src = '#'; // Clear preview if not an image
console.error('Please select a valid image file.');
}
}}
className='text-white' type='file' name='image' />
</div>
<div>
<label>Colour</label>
<input
onChange={e => setDetails((d: any) => {
return { ...d, color: e.target.value }
})}
type='color' name='color' />
</div>
{details && <RoomCard
color={details.color}
image={details!.image}
room='TEST'
title={details.title}
/>}
</div>
</div>
</div>
</div>
)
}
+38
View File
@@ -0,0 +1,38 @@
import Link from 'next/link'
import React from 'react'
export default function Profile() {
return (
<div className='w-screen h-screen bg-black'
// style={{ 'background': 'conic-gradient(from 210deg, #c6bcb9 0.000deg, #c6bcb9 24.000deg, #b9b6b9 calc(24.000deg + 0.1deg), #b9b6b9 48.000deg, #aab0b8 calc(48.000deg + 0.1deg), #aab0b8 72.000deg, #9ba9b6 calc(72.000deg + 0.1deg), #9ba9b6 96.000deg, #8ca2b4 calc(96.000deg + 0.1deg), #8ca2b4 120.000deg, #7e99b0 calc(120.000deg + 0.1deg), #7e99b0 144.000deg, #7191ac calc(144.000deg + 0.1deg), #7191ac 168.000deg, #6688a7 calc(168.000deg + 0.1deg), #6688a7 192.000deg, #5d7fa2 calc(192.000deg + 0.1deg), #5d7fa2 216.000deg, #56769c calc(216.000deg + 0.1deg), #56769c 240.000deg, #526d95 calc(240.000deg + 0.1deg), #526d95 264.000deg, #50648e calc(264.000deg + 0.1deg), #50648e 288.000deg, #515b86 calc(288.000deg + 0.1deg), #515b86 312.000deg, #55547e calc(312.000deg + 0.1deg), #55547e 336.000deg, #5c4c75 calc(336.000deg + 0.1deg) 360.000deg)' }}
>
<div className='grow flex flex-col items-center align-middle justify-center h-full w-full'>
<div
className='bg-white/10 w-full h-full lg:h-[90%] lg:w-[90%] m-2 backdrop-blur-lg flex flex-col border border-white/20 shadow-lg'
>
<div className='bg-purple-950 text-white flex justify-evenly'>
<button className='p-2 grow hover:backdrop-blur-lg hover:bg-white/10'>Room Details</button>
<button className='p-2 grow hover:backdrop-blur-lg hover:bg-white/10'>Audiance</button>
<button className='p-2 grow hover:backdrop-blur-lg hover:bg-white/10'>Rules</button>
<button className='p-2 grow hover:backdrop-blur-lg hover:bg-white/10'>Guest List</button>
</div>
</div>
</div>
</div>
)
}
function RoomCard({
title,
room,
}: {
title: string
room: string
}) {
return <Link href={`/testb?room=${room}`}><div>{title}</div></Link>
}