-
-
-
Dev Zero
-
-
- {/*
- Live Stage
- */}
-
- Devzero Stage
-
-
- Schedule
-
-
- Speakers
-
-
-
-
{ setDropdown(!dropdown) }}
- >
- {isActive('/') ? "Devzero Stage" : isActive('/schedule') ? "Schedule" : "Speakers"}
-
-
-
-
-
-
-
-
- Devzero Stage
-
-
-
-
- Schedule
-
-
-
-
- Speakers
-
-
-
-
-
+ return (
+
+
+
+
+ Dev
+ Zero
+
+
+
+
+ Live Stage
+
+
+ Devzero Stage
+
+
+ Schedule
+
+
+ Speakers
+
+
+
+
{
+ setDropdown(!dropdown);
+ }}
+ >
+ {isActive("/")
+ ? "Devzero Stage"
+ : isActive("/schedule")
+ ? "Schedule"
+ : isActive('/live-stage') ? "Live Stage" : "Speakers"}
+
+
+
+
+
+
+
+
+ Live Stage
+
+
+
+
+ Devzero Stage
+
+
+
+
+ Schedule
+
+
+
+
+ Speakers
+
+
+
- )
-}
+
+
+ );
+};
export default Navbar;
diff --git a/app/components/RoomContent.tsx b/app/components/RoomContent.tsx
new file mode 100644
index 0000000..7b7eec5
--- /dev/null
+++ b/app/components/RoomContent.tsx
@@ -0,0 +1,19 @@
+import Video from "./Video";
+import Footer from "./Footer";
+import ChatContent from "./ChatContent";
+
+const RoomContent = () => {
+ return (
+
+ )
+}
+
+export default RoomContent
diff --git a/app/components/ScheduleContent.tsx b/app/components/ScheduleContent.tsx
index 7be3a4a..7e38c32 100644
--- a/app/components/ScheduleContent.tsx
+++ b/app/components/ScheduleContent.tsx
@@ -11,7 +11,7 @@ const ScheduleContent = () => {
useEffect(() => {
getEvents().then((data) => {
- const eventArray = data.data;
+ const eventArray = data.events;
const liveTimingMap: timingMap = {};
const devzeroTimingMap: timingMap = {};
diff --git a/app/components/SpeakersContent.tsx b/app/components/SpeakersContent.tsx
index b795812..813a152 100644
--- a/app/components/SpeakersContent.tsx
+++ b/app/components/SpeakersContent.tsx
@@ -10,7 +10,7 @@ const SpeakersContent = () => {
useEffect(() => {
getSpeakers().then((data) => {
- setSpeakers(data.data);
+ setSpeakers(data.speakers);
});
}, []);
diff --git a/app/components/UserEntry.tsx b/app/components/UserEntry.tsx
new file mode 100644
index 0000000..64bb86f
--- /dev/null
+++ b/app/components/UserEntry.tsx
@@ -0,0 +1,35 @@
+"use client";
+
+import React, { useState } from "react";
+import { useRouter } from "next/navigation";
+
+const EntryComponent = () => {
+ const [name, setName] = useState("");
+ const router = useRouter();
+
+ const enterChatRoom = (e:React.FormEvent
) => {
+ e.preventDefault();
+ localStorage.setItem("name", name);
+ router.push(`/live-stage/a`);
+ };
+
+ return (
+
+ );
+};
+
+export default EntryComponent;
diff --git a/app/live-stage/[roomId]/page.tsx b/app/live-stage/[roomId]/page.tsx
new file mode 100644
index 0000000..dd5f670
--- /dev/null
+++ b/app/live-stage/[roomId]/page.tsx
@@ -0,0 +1,11 @@
+import RoomContent from "@/app/components/RoomContent";
+
+const page = () => {
+ return (
+
+
+
+ )
+}
+
+export default page
diff --git a/app/live-stage/page.tsx b/app/live-stage/page.tsx
new file mode 100644
index 0000000..745d0d7
--- /dev/null
+++ b/app/live-stage/page.tsx
@@ -0,0 +1,11 @@
+import LiveStageContent from "../components/LiveStageContent"
+
+const page = () => {
+ return (
+
+
+
+ )
+}
+
+export default page
diff --git a/controller/controller.spec.ts b/controller/controller.spec.ts
new file mode 100644
index 0000000..1cf0dc5
--- /dev/null
+++ b/controller/controller.spec.ts
@@ -0,0 +1,174 @@
+import {
+ getEventsFromSupabase,
+ getSpeakersFromSupabase,
+} from "@/controller/controller";
+import { supabase } from "@/lib/supabaseClient";
+import { PostgrestError } from "@supabase/supabase-js";
+import redis from "@/lib/redisClient";
+import { UnhandledError } from "@/errors/databaseerror";
+
+jest.mock("../lib/supabaseClient", () => ({
+ supabase: {
+ from: jest.fn().mockReturnThis(),
+ select: jest.fn(),
+ },
+}));
+
+jest.mock("../lib/redisClient", () => ({
+ get: jest.fn(),
+ set: jest.fn(),
+}));
+
+describe("getEventsFromSupabase", () => {
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it("should return cached data if available", async () => {
+ const cachedData = [{ event_id: 1, event_title: "Event 1" }];
+ jest.spyOn(redis, "get").mockResolvedValueOnce(JSON.stringify(cachedData));
+
+ const result = await getEventsFromSupabase();
+
+ expect(redis.get).toHaveBeenCalledWith("events");
+ expect(supabase.from).not.toHaveBeenCalled();
+ expect(result).toEqual(cachedData);
+ });
+
+ it("should fetch data from Supabase if cache is empty", async () => {
+ const supabaseData = [{ event_id: 1, event_title: "Event 1" }];
+ const supabaseResponse = { data: supabaseData, error: null };
+ jest.spyOn(redis, "get").mockResolvedValueOnce(null);
+ (supabase.from("events").select as jest.Mock).mockReturnValueOnce(
+ supabaseResponse
+ );
+
+ const result = await getEventsFromSupabase();
+
+ expect(redis.get).toHaveBeenCalledWith("events");
+ expect(supabase.from).toHaveBeenCalledWith("events");
+ expect(redis.set).toHaveBeenCalledWith(
+ "events",
+ JSON.stringify(supabaseData),
+ "EX",
+ 600
+ );
+ expect(result).toEqual(supabaseData);
+ });
+
+ it("should throw an error if Supabase returns an error", async () => {
+ const supabaseError: PostgrestError = {
+ code: "PGRST200",
+ message: "Table not found",
+ hint: "",
+ details: "",
+ };
+ const supabaseResponse = { data: null, error: supabaseError };
+ jest.spyOn(redis, "get").mockResolvedValueOnce(null);
+ (supabase.from("events").select as jest.Mock).mockReturnValueOnce(
+ supabaseResponse
+ );
+
+ try {
+ await getEventsFromSupabase();
+ } catch (error) {
+ expect(error).toBeInstanceOf(UnhandledError);
+ }
+
+ expect(redis.get).toHaveBeenCalledWith("events");
+ expect(supabase.from).toHaveBeenCalledWith("events");
+ expect(redis.set).not.toHaveBeenCalled();
+ });
+
+ it("should throw an UnhandledError if an unexpected error occurs", async () => {
+ const unexpectedError = new Error("Unexpected error");
+ jest.spyOn(redis, "get").mockRejectedValueOnce(unexpectedError);
+
+ try {
+ await getEventsFromSupabase();
+ } catch (error) {
+ expect(error).toBeInstanceOf(UnhandledError);
+ }
+
+ expect(redis.get).toHaveBeenCalledWith("events");
+ expect(supabase.from).not.toHaveBeenCalled();
+ expect(redis.set).not.toHaveBeenCalled();
+ });
+});
+
+describe("getSpeakersFromSupabase", () => {
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it("should return cached data if available", async () => {
+ const cachedData = [{ speaker_id: 1, speaker_name: "Speaker 1" }];
+ jest.spyOn(redis, "get").mockResolvedValueOnce(JSON.stringify(cachedData));
+
+ const result = await getSpeakersFromSupabase();
+
+ expect(redis.get).toHaveBeenCalledWith("speakers");
+ expect(supabase.from).not.toHaveBeenCalled();
+ expect(result).toEqual(cachedData);
+ });
+
+ it("should fetch data from Supabase if cache is empty", async () => {
+ const supabaseData = [{ speaker_id: 1, speaker_name: "Speaker 1" }];
+ const supabaseResponse = { data: supabaseData, error: null };
+ jest.spyOn(redis, "get").mockResolvedValueOnce(null);
+ (supabase.from("speakers").select as jest.Mock).mockReturnValueOnce(
+ supabaseResponse
+ );
+
+ const result = await getSpeakersFromSupabase();
+
+ expect(redis.get).toHaveBeenCalledWith("speakers");
+ expect(supabase.from).toHaveBeenCalledWith("speakers");
+ expect(redis.set).toHaveBeenCalledWith(
+ "speakers",
+ JSON.stringify(supabaseData),
+ "EX",
+ 600
+ );
+ expect(result).toEqual(supabaseData);
+ });
+
+ it("should throw an error if Supabase returns an error", async () => {
+ const supabaseError: PostgrestError = {
+ code: "PGRST200",
+ message: "Table not found",
+ hint: "",
+ details: "",
+ };
+ const supabaseResponse = { data: null, error: supabaseError };
+ jest.spyOn(redis, "get").mockResolvedValueOnce(null);
+ (supabase.from("speakers").select as jest.Mock).mockReturnValueOnce(
+ supabaseResponse
+ );
+
+ try {
+ await getSpeakersFromSupabase();
+ } catch (error) {
+ expect(error).toBeInstanceOf(UnhandledError);
+ }
+
+ expect(redis.get).toHaveBeenCalledWith("speakers");
+ expect(supabase.from).toHaveBeenCalledWith("speakers");
+ expect(redis.set).not.toHaveBeenCalled();
+ });
+
+ it("should throw an UnhandledError if an unexpected error occurs", async () => {
+ const unexpectedError = new Error("Unexpected error");
+ jest.spyOn(redis, "get").mockRejectedValueOnce(unexpectedError);
+
+ try {
+ await getSpeakersFromSupabase();
+ } catch (error) {
+ expect(error).toBeInstanceOf(UnhandledError);
+ }
+
+ expect(redis.get).toHaveBeenCalledWith("speakers");
+ expect(supabase.from).not.toHaveBeenCalled();
+ expect(redis.set).not.toHaveBeenCalled();
+ });
+});
diff --git a/controller/controller.ts b/controller/controller.ts
new file mode 100644
index 0000000..dc7a09f
--- /dev/null
+++ b/controller/controller.ts
@@ -0,0 +1,84 @@
+import { supabase } from "@/lib/supabaseClient";
+import {
+ SupabaseError,
+ AccessDeniedError,
+ UnhandledError,
+ NotAuthenticatedError,
+} from "@/errors/databaseerror";
+import redis from "@/lib/redisClient";
+
+export async function getEventsFromSupabase() {
+
+ const cacheKey = 'events';
+
+ try {
+
+ let cachedData = await redis.get(cacheKey);
+ if(cachedData) {
+ return JSON.parse(cachedData);
+ }
+
+ const { data, error } = await supabase.from("events").select(`
+ event_id,
+ event_title,
+ event_timing,
+ event_type,
+ created_at,
+ updated_at,
+ speakers (
+ speaker_name
+ )
+ `);
+ if (error) {
+ switch (error.code) {
+ case "PGRST200":
+ throw new SupabaseError("Table not found");
+ case "PGRST302":
+ throw new AccessDeniedError("Access Denied");
+ case "PGRST301":
+ throw new NotAuthenticatedError("Not Authenticated");
+ default:
+ throw new UnhandledError("Internal server error");
+ }
+ } else {
+ await redis.set(cacheKey, JSON.stringify(data), 'EX', 600);
+ }
+ return data;
+ } catch (error) {
+ console.error("Error fetching events from Supabase:", error);
+ throw new UnhandledError("Internal server error");
+ }
+}
+
+export async function getSpeakersFromSupabase() {
+
+ const cacheKey = 'speakers';
+
+ try {
+
+ let cachedData = await redis.get(cacheKey);
+ if(cachedData) {
+ return JSON.parse(cachedData);
+ }
+
+ const { data, error } = await supabase.from('speakers').select('*');
+ if (error) {
+ switch (error.code) {
+ case "PGRST200":
+ throw new SupabaseError("Table not found");
+ case "PGRST302":
+ throw new AccessDeniedError("Access Denied");
+ case "PGRST301":
+ throw new NotAuthenticatedError("Not Authenticated");
+ default:
+ throw new UnhandledError("Internal server error");
+ }
+ } else {
+ await redis.set(cacheKey, JSON.stringify(data), 'EX', 600);
+ }
+ return data;
+ } catch (error) {
+ console.error('Error fetching speakers from Supabase:', error);
+ throw new UnhandledError('Internal server error');
+ }
+}
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 0000000..9d690d0
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,22 @@
+version: '3.8'
+services:
+ app:
+ build: .
+ container_name: nextjs-dind-app-virtual-event
+ privileged: true
+ environment:
+ - DOCKER_TLS_CERTDIR=/certs
+ - REDIS_URL=redis://redis-container:6379
+ volumes:
+ - /var/lib/docker
+ ports:
+ - '3000:3000'
+ - '54321:54321'
+ depends_on:
+ - redis-container
+
+ redis-container:
+ image: redis
+ container_name: nextjs-redis
+ ports:
+ - '6379:6379'
diff --git a/entrypoint.sh b/entrypoint.sh
new file mode 100644
index 0000000..d703db6
--- /dev/null
+++ b/entrypoint.sh
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+echo "Waiting for the Docker daemon to start..."
+while ! docker info >/dev/null 2>&1; do
+ sleep 1
+done
+echo "Docker daemon is up and running!"
+
+npx supabase start -x imgproxy,migra,studio,edge-runtime,logflare,vector,pgbouncer > start.txt
+
+echo "NEXT_PUBLIC_SUPABASE_URL=$(awk '/API URL:/ {print $3}' start.txt)" > .env.local
+echo "NEXT_PUBLIC_SUPABASE_ANON_KEY=$(awk '/anon key:/ {print $3}' start.txt)" >> .env.local
+
+exec npm run startapp
\ No newline at end of file
diff --git a/errors/databaseerror.ts b/errors/databaseerror.ts
index 3cff5ab..55c24bd 100644
--- a/errors/databaseerror.ts
+++ b/errors/databaseerror.ts
@@ -1,35 +1,32 @@
-class DatabaseErrror extends Error {
- status: string;
- message: string;
-
- constructor(status: string, message: string) {
+class DatabaseError extends Error {
+ code: string;
+
+ constructor(message: string, code: string) {
super(message);
- this.status = status;
- this.message = message;
+ this.code = code;
}
}
-class NotAuthenticatedError extends DatabaseErrror {
- constructor() {
- super('AUTH_DENIED', 'User not authenticated.');
+class NotAuthenticatedError extends DatabaseError {
+ constructor(message: string) {
+ super(message, 'AUTH_DENIED');
}
}
-class AccessDeniedError extends DatabaseErrror {
- constructor() {
- super('ACCESS_DENIED', 'Access to this resource denied.');
+class AccessDeniedError extends DatabaseError {
+ constructor(message: string) {
+ super(message, 'ACCESS_DENIED');
}
}
-class SupabaseError extends DatabaseErrror {
- constructor() {
- super('SUPABASE_ERR', ` Supabase error occurred.`);
+class SupabaseError extends DatabaseError {
+ constructor(message: string) {
+ super(message, 'SUPABASE_ERR');
}
}
-
-class UnhandledError extends DatabaseErrror {
- constructor() {
- super("UNHANDLED_ERR", 'Internal Server Error.');
+class UnhandledError extends DatabaseError {
+ constructor(message: string) {
+ super(message, 'UNHANDLED_ERROR');
}
}
diff --git a/__tests__/http-api.test.ts b/http/http-api.spec.ts
similarity index 52%
rename from __tests__/http-api.test.ts
rename to http/http-api.spec.ts
index 47cf054..9705627 100644
--- a/__tests__/http-api.test.ts
+++ b/http/http-api.spec.ts
@@ -1,4 +1,4 @@
-import { getEvents } from "@/http/api";
+import { getEvents, getSpeakers } from "@/http/api";
global.fetch = jest.fn();
@@ -42,3 +42,41 @@ describe("getEvents function", () => {
}
});
});
+
+describe("getSpeakers function", () => {
+ it("should fetch speakers correctly", async () => {
+ const mockData = {
+ message: "retrieved data successfully",
+ data: [
+ {
+ id: "a7efc63d-3e11-4bcb-b414-854235f7a5d6",
+ speaker_name: "John Doe",
+ position: "software engineer",
+ created_at: "2021-02-01T00:00:00.000Z",
+ updated_at: "2021-02-01T00:00:00.000Z",
+ },
+ ],
+ status: 200,
+ };
+
+ const mockResponse = { json: jest.fn().mockResolvedValue(mockData) };
+ (global.fetch as jest.Mock).mockResolvedValue(mockResponse);
+
+ const result = await getSpeakers();
+
+ expect(result).toEqual(mockData);
+ expect(global.fetch).toHaveBeenCalledWith("/api/speakers");
+ });
+
+ it("should throw an error if fetch fails", async () => {
+ const mockError = new Error("Failed to fetch");
+ (global.fetch as jest.Mock).mockRejectedValue(mockError);
+
+ try {
+ await getSpeakers();
+ } catch (error) {
+ expect(error).toEqual(mockError);
+ }
+ });
+});
+
diff --git a/lib/redisClient.ts b/lib/redisClient.ts
new file mode 100644
index 0000000..6a81f91
--- /dev/null
+++ b/lib/redisClient.ts
@@ -0,0 +1,6 @@
+import Redis from "ioredis";
+
+const redisUrl = process.env.REDIS_URL as string;
+const redisClient = new Redis(redisUrl);
+
+export default redisClient;
diff --git a/package-lock.json b/package-lock.json
index 2af1c39..61f76e7 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -14,6 +14,8 @@
"@mui/material": "^5.15.10",
"@supabase/ssr": "^0.1.0",
"@supabase/supabase-js": "^2.39.6",
+ "dotenv": "^16.4.4",
+ "ioredis": "^5.3.2",
"next": "14.1.0",
"react": "^18",
"react-dom": "^18",
@@ -1026,6 +1028,11 @@
"integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==",
"dev": true
},
+ "node_modules/@ioredis/commands": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.2.0.tgz",
+ "integrity": "sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg=="
+ },
"node_modules/@isaacs/cliui": {
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
@@ -3484,6 +3491,14 @@
"node": ">=6"
}
},
+ "node_modules/cluster-key-slot": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz",
+ "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/cmd-shim": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/cmd-shim/-/cmd-shim-6.0.2.tgz",
@@ -3865,6 +3880,14 @@
"node": ">=0.4.0"
}
},
+ "node_modules/denque": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz",
+ "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==",
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
"node_modules/dequal": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
@@ -3973,6 +3996,17 @@
"node": ">=12"
}
},
+ "node_modules/dotenv": {
+ "version": "16.4.4",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.4.tgz",
+ "integrity": "sha512-XvPXc8XAQThSjAbY6cQ/9PcBXmFoWuw1sQ3b8HqUCR6ziGXjkTi//kB9SWa2UwqlgdAIuRqAa/9hVljzPehbYg==",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://dotenvx.com"
+ }
+ },
"node_modules/eastasianwidth": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
@@ -5494,6 +5528,29 @@
"node": ">= 0.4"
}
},
+ "node_modules/ioredis": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.3.2.tgz",
+ "integrity": "sha512-1DKMMzlIHM02eBBVOFQ1+AolGjs6+xEcM4PDL7NqOS6szq7H9jSaEkIUH6/a5Hl241LzW6JLSiAbNvTQjUupUA==",
+ "dependencies": {
+ "@ioredis/commands": "^1.1.1",
+ "cluster-key-slot": "^1.1.0",
+ "debug": "^4.3.4",
+ "denque": "^2.1.0",
+ "lodash.defaults": "^4.2.0",
+ "lodash.isarguments": "^3.1.0",
+ "redis-errors": "^1.2.0",
+ "redis-parser": "^3.0.0",
+ "standard-as-callback": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=12.22.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/ioredis"
+ }
+ },
"node_modules/is-arguments": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz",
@@ -6912,6 +6969,16 @@
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"dev": true
},
+ "node_modules/lodash.defaults": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
+ "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ=="
+ },
+ "node_modules/lodash.isarguments": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz",
+ "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg=="
+ },
"node_modules/lodash.merge": {
"version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
@@ -8088,6 +8155,25 @@
"node": ">=8"
}
},
+ "node_modules/redis-errors": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz",
+ "integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/redis-parser": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz",
+ "integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==",
+ "dependencies": {
+ "redis-errors": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/reflect.getprototypeof": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.5.tgz",
@@ -8524,6 +8610,11 @@
"node": ">=8"
}
},
+ "node_modules/standard-as-callback": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz",
+ "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A=="
+ },
"node_modules/stop-iteration-iterator": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz",
diff --git a/package.json b/package.json
index 53b0703..cde7e43 100644
--- a/package.json
+++ b/package.json
@@ -7,7 +7,8 @@
"build": "next build",
"start": "next start",
"lint": "next lint",
- "test": "jest"
+ "test": "jest",
+ "startapp": "node scripts/db-seed.js && npm run build && npm run start"
},
"dependencies": {
"@emotion/react": "^11.11.3",
@@ -16,6 +17,8 @@
"@mui/material": "^5.15.10",
"@supabase/ssr": "^0.1.0",
"@supabase/supabase-js": "^2.39.6",
+ "dotenv": "^16.4.4",
+ "ioredis": "^5.3.2",
"next": "14.1.0",
"react": "^18",
"react-dom": "^18",
diff --git a/scripts/db-seed.js b/scripts/db-seed.js
new file mode 100644
index 0000000..e4547f8
--- /dev/null
+++ b/scripts/db-seed.js
@@ -0,0 +1,167 @@
+const cl = require('@supabase/supabase-js');
+require('dotenv').config({ path: './.env.local' });
+
+const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;
+console.log(supabaseUrl)
+const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;
+const supabase = cl.createClient(supabaseUrl, supabaseAnonKey);
+
+async function insertSpeakers() {
+ const speakers = [
+ {
+ "id": "84b9179e-218d-4a4d-adc4-e69272e6769b",
+ "speaker_name": "Aaron",
+ "position": "Network Engineer"
+ },
+ {
+ "id": "32445e61-acc6-4c6e-9b2a-c4cf563e3543",
+ "speaker_name": "Michael",
+ "position": "Database Administrator"
+ },
+ {
+ "id": "8a2ccff7-e4a5-4304-a05d-6b6c22754c99",
+ "speaker_name": "Olive",
+ "position": "IT Consultant"
+ },
+ {
+ "id": "0cc67de5-c421-405c-8c9e-013c71efcab9",
+ "speaker_name": "John",
+ "position": "Software Engineer"
+ },
+ {
+ "id": "5db645c6-acd4-4dde-8c84-1a4d14cd7cfb",
+ "speaker_name": "Jamie",
+ "position": "Data Analyst"
+ },
+ {
+ "id": "36b08404-b9c8-4c8d-9adc-e9697c2447ca",
+ "speaker_name": "Alex",
+ "position": "Web Developer"
+ },
+ {
+ "id": "775ef0f0-f3ff-40a0-9ae8-8e4bfcf5cfca",
+ "speaker_name": "Ellum",
+ "position": "Network Engineer"
+ },
+ {
+ "id": "8985f5b4-9fcf-44c4-8d9a-66b0a4bd038f",
+ "speaker_name": "William",
+ "position": "Cybersecurity Analyst"
+ },
+ ];
+
+ for (const speaker of speakers) {
+ const { data, error } = await supabase
+ .from('speakers')
+ .insert([
+ {
+ id: speaker.id,
+ speaker_name: speaker.speaker_name,
+ position: speaker.position,
+ created_at: new Date(),
+ updated_at: new Date()
+ }
+ ]);
+
+ if (error) {
+ console.error('Error:', error);
+ break;
+ }
+ }
+}
+
+async function insertEvents() {
+ const events = [
+ {
+ speaker_id: "84b9179e-218d-4a4d-adc4-e69272e6769b",
+ event_title: "How to Create a Next.js App",
+ event_timing: "4:00 PM - 6:00 PM",
+ event_type: "Live Stage"
+ },
+ {
+ speaker_id: "32445e61-acc6-4c6e-9b2a-c4cf563e3543",
+ event_title: "How to Create a React.js App",
+ event_timing: "4:00 PM - 6:00 PM",
+ event_type: "Live Stage"
+ },
+ {
+ speaker_id: "8a2ccff7-e4a5-4304-a05d-6b6c22754c99",
+ event_title: "How to Create a CRUD application",
+ event_timing: "7:00 PM - 8:00 PM",
+ event_type: "Live Stage"
+ },
+ {
+ speaker_id: "0cc67de5-c421-405c-8c9e-013c71efcab9",
+ event_title: "How to Dockerize Your Applications",
+ event_timing: "4:00 PM - 6:00 PM",
+ event_type: "DevZero Stage"
+ },
+ {
+ speaker_id: "5db645c6-acd4-4dde-8c84-1a4d14cd7cfb",
+ event_title: "Mastering Kubernetes for DevOps",
+ event_timing: "4:00 PM - 6:00 PM",
+ event_type: "DevZero Stage"
+ },
+ {
+ speaker_id: "36b08404-b9c8-4c8d-9adc-e9697c2447ca",
+ event_title: "Introduction to Machine Learning",
+ event_timing: "7:00 PM - 8:00 PM",
+ event_type: "DevZero Stage"
+ },
+ {
+ speaker_id: "775ef0f0-f3ff-40a0-9ae8-8e4bfcf5cfca",
+ event_title: "Building RESTful APIs with Node.js",
+ event_timing: "7:00 PM - 8:00 PM",
+ event_type: "DevZero Stage"
+ },
+ {
+ speaker_id: "8985f5b4-9fcf-44c4-8d9a-66b0a4bd038f",
+ event_title: "Effective Agile Project Management",
+ event_timing: "7:00 PM - 8:00 PM",
+ event_type: "DevZero Stage"
+ },
+ {
+ speaker_id: "8985f5b4-9fcf-44c4-8d9a-66b0a4bd038f",
+ event_title: "Cloud Security Best Practices",
+ event_timing: "9:00 PM - 10:00 PM",
+ event_type: "DevZero Stage"
+ },
+ {
+ speaker_id: "775ef0f0-f3ff-40a0-9ae8-8e4bfcf5cfca",
+ event_title: "Using GraphQL with React.js",
+ event_timing: "9:00 PM - 10:00 PM",
+ event_type: "DevZero Stage"
+ },
+ ];
+
+ for (const event of events) {
+ const { data, error } = await supabase
+ .from('events')
+ .insert([
+ {
+ speaker_id: event.speaker_id,
+ event_title: event.event_title,
+ event_timing: event.event_timing,
+ event_type: event.event_type,
+ created_at: new Date(),
+ updated_at: new Date()
+ }
+ ]);
+
+ if (error) {
+ console.error('Error:', error);
+ break;
+ }
+ }
+}
+
+
+(async () => {
+ try {
+ await insertSpeakers();
+ await insertEvents();
+ console.log('Data inserted successfully');
+ } catch (error) {
+ console.error('Error:', error);
+ }
+})();
\ No newline at end of file
diff --git a/tailwind.config.ts b/tailwind.config.ts
index f54a0c6..c2f1f7d 100644
--- a/tailwind.config.ts
+++ b/tailwind.config.ts
@@ -12,12 +12,13 @@ const config: Config = {
"gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
"gradient-conic":
"conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))",
+ "text-gradient": "linear-gradient(to right, #e73c9f, #a032c6, #4125f8)",
},
colors: {
- "cus-purple": "#0f0b29",
+ "cus-purple": "#0f0b29",
"cus-purple-light": "#412666",
- "cus-text": '#bbb0ee',
- }
+ "cus-text": "#bbb0ee",
+ },
},
},
plugins: [],
diff --git a/utils/controller.ts b/utils/controller.ts
deleted file mode 100644
index 3c12f2a..0000000
--- a/utils/controller.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-import { supabase } from "@/lib/supabaseClient";
-
-export async function getEventsFromSupabase() {
- const { data, error } = await supabase.from("events").select(`
- event_id,
- event_title,
- event_timing,
- event_type,
- created_at,
- updated_at,
- speakers (
- speaker_name
- )
- `);
- return data;
-}
-
-export async function getSpeakersFromSupabase() {
- const { data, error } = await supabase.from("speakers").select("*");
- return data;
-}