diff --git a/.gitignore b/.gitignore
index 20a7a476..09df3ea1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,5 @@
+**/.DS_Store
+
.env.local
.env.*.local
node_modules
diff --git a/src/.DS_Store b/src/.DS_Store
deleted file mode 100644
index acb79913..00000000
Binary files a/src/.DS_Store and /dev/null differ
diff --git a/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/(specific-project-pages)/chats/[chatId]/page.tsx b/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/(specific-project-pages)/chats/[chatId]/page.tsx
deleted file mode 100644
index a893898b..00000000
--- a/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/(specific-project-pages)/chats/[chatId]/page.tsx
+++ /dev/null
@@ -1,35 +0,0 @@
-import { ChatContainer } from "@/components/chat-container";
-import { getChatById } from "@/data/user/chats";
-import { getSlimProjectBySlug } from "@/data/user/projects";
-
-import { nanoid, type Message } from "ai";
-
-
-
-export default async function ChatPage({ params }: { params: { chatId: string; projectSlug: string } }) {
- const { chatId, projectSlug } = params;
- const project = await getSlimProjectBySlug(projectSlug);
-
- if (!chatId) {
- const newChatId = nanoid();
- return ;
- }
-
- const chat = await getChatById(chatId);
-
- if (chat.payload !== null && typeof chat.payload === "string") {
- const { messages } = JSON.parse(chat.payload);
- const assertedMessages = messages as unknown as Message[];
- return (
-
-
-
- );
- }
-
- return ;
-}
diff --git a/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/(specific-project-pages)/chats/page.tsx b/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/(specific-project-pages)/chats/page.tsx
deleted file mode 100644
index 0254a0a6..00000000
--- a/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/(specific-project-pages)/chats/page.tsx
+++ /dev/null
@@ -1,12 +0,0 @@
-import { ChatHistory } from "@/components/chat-history";
-import { getSlimProjectBySlug } from "@/data/user/projects";
-
-
-export default async function ChatsPage({ params }: { params: { projectSlug: string } }) {
- const { projectSlug } = params;
- const project = await getSlimProjectBySlug(projectSlug);
-
-
- return ;
-}
-
diff --git a/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/(specific-project-pages)/image-generator/page.tsx b/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/(specific-project-pages)/image-generator/page.tsx
deleted file mode 100644
index 995882e0..00000000
--- a/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/(specific-project-pages)/image-generator/page.tsx
+++ /dev/null
@@ -1,11 +0,0 @@
-import { DallE } from "@/components/dall-e";
-
-export default async function ImageGeneratorPage() {
-
- return (
-
-
-
- )
-}
-
diff --git a/src/app/api/chat/route.ts b/src/app/api/chat/route.ts
deleted file mode 100644
index e7b689d2..00000000
--- a/src/app/api/chat/route.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import { openai } from '@ai-sdk/openai';
-import { StreamingTextResponse, streamText } from 'ai';
-
-export async function POST(req: Request) {
- const { messages } = await req.json();
-
- const result = await streamText({
- model: openai('gpt-3.5-turbo'),
- messages,
- });
-
- return new StreamingTextResponse(result.toAIStream());
-}
diff --git a/src/components/chat-container.tsx b/src/components/chat-container.tsx
deleted file mode 100644
index cfec44b9..00000000
--- a/src/components/chat-container.tsx
+++ /dev/null
@@ -1,98 +0,0 @@
-'use client';
-
-import { useChat, type Message } from 'ai/react';
-
-import { insertChat } from '@/data/user/chats';
-import { useSAToastMutation } from '@/hooks/useSAToastMutation';
-import { cn } from '@/lib/utils';
-import { nanoid } from 'nanoid';
-import { usePathname } from 'next/navigation';
-import { toast } from 'sonner';
-import { ChatList } from './chat-list';
-import { ChatPanel } from './chat-panel';
-import { ChatScrollAnchor } from './chat-scroll-anchor';
-import { EmptyScreen } from './empty-screen';
-
-export interface ChatProps extends React.ComponentProps<'div'> {
- initialMessages?: Message[];
- id?: string;
- project: { id: string, slug: string, name: string };
-}
-
-
-export function ChatContainer({ id, initialMessages, className, project }: ChatProps) {
- const { mutate } = useSAToastMutation(async ({ chatId, projectId, content }: { chatId: string, projectId: string, content: Message[] }) => {
- return await insertChat(projectId, content, chatId);
- }, {
- errorMessage(error) {
- try {
- if (error instanceof Error) {
- return String(error.message);
- }
- return `Failed to delete organization ${String(error)}`;
- } catch (_err) {
- console.warn(_err);
- return 'Failed to delete organization';
- }
- },
- })
-
- const pathname = usePathname();
-
- const { messages, append, reload, stop, isLoading, input, setInput } =
- useChat({
- initialMessages,
- id,
- body: {
- id,
- },
- onFinish({ content }) {
- messages.push({
- role: 'user',
- content: input,
- id: nanoid(),
- }, {
- role: 'assistant',
- content,
- id: nanoid(),
- });
-
- if (pathname === `/project/${project.slug}`) {
- const chatPath = `/project/${project.slug}/chats/${id}`;
- window.history.replaceState(null, '', chatPath);
- }
- mutate({ chatId: id ?? nanoid(), projectId: project.id, content: messages });
- },
- onResponse(response) {
- if (response.status === 401) {
- toast.error(response.statusText);
- }
- },
- });
-
- return (
- <>
-
- {messages.length ? (
- <>
-
-
- >
- ) : (
-
- )}
-
-
- >
- );
-}
diff --git a/src/components/chat-history.tsx b/src/components/chat-history.tsx
deleted file mode 100644
index d8a4d7b0..00000000
--- a/src/components/chat-history.tsx
+++ /dev/null
@@ -1,97 +0,0 @@
-
-import Link from "next/link";
-
-import { buttonVariants } from "@/components/ui/button";
-
-import { cn } from "@/lib/utils";
-import { serverGetLoggedInUser } from "@/utils/server/serverGetLoggedInUser";
-import { PlusIcon } from "@radix-ui/react-icons";
-
-import { getChats, getChatsHistory } from "@/data/user/chats";
-import { getSlimProjectById } from "@/data/user/projects";
-import type { Message } from "ai";
-import { formatRelative, subDays } from "date-fns";
-import { HomeIcon } from "lucide-react";
-
-async function ChatList({ userId }: { userId: string }) {
- const chats = await getChats(userId);
- return (
-
- {chats.map((chat) => {
- const { payload } = chat;
- const messages =
- typeof payload === "object" &&
- payload !== null &&
- "messages" in payload
- ? payload.messages
- : [];
- const assertedMessages = messages as unknown as Message[];
- const title =
- assertedMessages.length > 0
- ? assertedMessages[0].content
- : "No title";
-
- return (
-
- {title}
-
- );
- })}
-
- );
-}
-
-export async function ChatHistory({ projectId }: { projectId: string }) {
- const user = await serverGetLoggedInUser();
- const project = await getSlimProjectById(projectId)
- const userId = user.id;
- const chatsHistory = await getChatsHistory(projectId, userId)
- return (
-
-
-
-
- Home
-
-
-
-
-
- {chatsHistory.map((chat, i) => (
-
- Chat {chat.id} - {formatRelative(subDays(new Date(), 3), new Date(chat.created_at))}
-
- ))}
-
-
-
- );
-}
diff --git a/src/components/chat-list.tsx b/src/components/chat-list.tsx
deleted file mode 100644
index 1c3b3f20..00000000
--- a/src/components/chat-list.tsx
+++ /dev/null
@@ -1,30 +0,0 @@
-import type { Message } from "ai";
-
-import { Separator } from "@/components/ui/separator";
-import { ChatMessage } from "./chat-message";
-import { ScrollArea } from "./ui/scroll-area";
-
-export interface ChatList {
- messages: Message[];
-}
-
-export function ChatList({ messages }: ChatList) {
- if (!messages.length) {
- return null;
- }
-
- return (
-
-
- {messages.map((message, index) => (
-
-
- {index < messages.length - 1 && (
-
- )}
-
- ))}
-
-
- );
-}
diff --git a/src/components/chat-message-actions.tsx b/src/components/chat-message-actions.tsx
deleted file mode 100644
index 91eaa1f1..00000000
--- a/src/components/chat-message-actions.tsx
+++ /dev/null
@@ -1,40 +0,0 @@
-"use client";
-
-import type { Message } from "ai";
-
-import { Button } from "@/components/ui/button";
-import { useCopyToClipboard } from "@/hooks/useCopyToClipboard";
-import { cn } from "@/lib/utils";
-import { Check, Copy } from "lucide-react";
-
-interface ChatMessageActionsProps extends React.ComponentProps<"div"> {
- message: Message;
-}
-
-export function ChatMessageActions({
- message,
- className,
- ...props
-}: ChatMessageActionsProps) {
- const { isCopied, copyToClipboard } = useCopyToClipboard({ timeout: 2000 });
-
- const onCopy = () => {
- if (isCopied) return;
- copyToClipboard(message.content);
- };
-
- return (
-
-
-
- );
-}
diff --git a/src/components/chat-message.tsx b/src/components/chat-message.tsx
deleted file mode 100644
index 6d90776f..00000000
--- a/src/components/chat-message.tsx
+++ /dev/null
@@ -1,69 +0,0 @@
-import type { Message } from "ai";
-import remarkGfm from "remark-gfm";
-import remarkMath from "remark-math";
-
-import { ChatMessageActions } from "@/components/chat-message-actions";
-import { MemoizedReactMarkdown } from "@/components/markdown";
-import { cn } from "@/lib/utils";
-import { Bot, User } from "lucide-react";
-import { CodeBlock } from "./ui/codeblock";
-
-
-export interface ChatMessageProps {
- message: Message;
-}
-
-export function ChatMessage({ message, ...props }: ChatMessageProps) {
- return (
-
-
- {message.role === "user" ? : }
-
-
- {children};
- },
- // @ts-expect-error - `node` is not used
- code({ node, inline, className, children, ...props }) {
- const match = /language-(\w+)/.exec(className || "");
-
- if (inline) {
- return (
-
- {children}
-
- );
- }
-
- return (
-
- );
- },
- }}
- >
- {message.content}
-
-
-
-
- );
-}
diff --git a/src/components/chat-panel.tsx b/src/components/chat-panel.tsx
deleted file mode 100644
index 8f91517a..00000000
--- a/src/components/chat-panel.tsx
+++ /dev/null
@@ -1,79 +0,0 @@
-import type { UseChatHelpers } from "ai/react";
-
-import { PromptForm } from "@/components/prompt-form";
-import { Button } from "@/components/ui/button";
-import { RefreshCwIcon, StopCircle } from "lucide-react";
-import { ButtonScrollToBottom } from "./button-scroll-to-bottom";
-
-export interface ChatPanelProps
- extends Pick<
- UseChatHelpers,
- | "append"
- | "isLoading"
- | "reload"
- | "messages"
- | "stop"
- | "input"
- | "setInput"
- > {
- id?: string;
- projectSlug: string;
-}
-
-export function ChatPanel({
- id,
- isLoading,
- stop,
- append,
- reload,
- input,
- setInput,
- messages,
- projectSlug,
-}: ChatPanelProps) {
- return (
-
-
-
-
- {isLoading ? (
-
- ) : (
- messages?.length > 0 && (
-
- )
- )}
-
-
-
{
- await append({
- id,
- content: value,
- role: "user",
- });
- }}
- input={input}
- setInput={setInput}
- isLoading={isLoading}
- />
-
-
-
- );
-}
diff --git a/src/components/chat-scroll-anchor.tsx b/src/components/chat-scroll-anchor.tsx
deleted file mode 100644
index 08ebc71c..00000000
--- a/src/components/chat-scroll-anchor.tsx
+++ /dev/null
@@ -1,28 +0,0 @@
-'use client';
-
-import { useAtBottom } from '@/hooks/useAtBottom';
-import * as React from 'react';
-import { useInView } from 'react-intersection-observer';
-
-interface ChatScrollAnchorProps {
- trackVisibility?: boolean;
-}
-
-export function ChatScrollAnchor({ trackVisibility }: ChatScrollAnchorProps) {
- const isAtBottom = useAtBottom();
- const { ref, entry, inView } = useInView({
- trackVisibility,
- delay: 100,
- rootMargin: '0px 0px -150px 0px',
- });
-
- React.useEffect(() => {
- if (isAtBottom && trackVisibility && !inView) {
- entry?.target.scrollIntoView({
- block: 'start',
- });
- }
- }, [inView, entry, isAtBottom, trackVisibility]);
-
- return ;
-}
diff --git a/src/components/dall-e.tsx b/src/components/dall-e.tsx
deleted file mode 100644
index 7f40171a..00000000
--- a/src/components/dall-e.tsx
+++ /dev/null
@@ -1,165 +0,0 @@
-"use client";
-import { convertAndUploadOpenAiImage } from "@/data/user/chats";
-import { updateUserProfileNameAndAvatar } from "@/data/user/user";
-import { useSAToastMutation } from "@/hooks/useSAToastMutation";
-import type { SAPayload } from "@/types";
-import { zodResolver } from "@hookform/resolvers/zod";
-import { useMutation } from "@tanstack/react-query";
-import axios from "axios";
-import { CircleUserRound, Copy, Loader } from "lucide-react";
-import Image from "next/image";
-import { useState } from "react";
-import { Controller, useForm, type SubmitHandler } from "react-hook-form";
-import { toast } from "sonner";
-import { z } from "zod";
-import { Button } from "./ui/button";
-import { Input } from "./ui/input";
-import { Label } from "./ui/label";
-import {
- Select,
- SelectContent,
- SelectItem,
- SelectTrigger,
- SelectValue,
-} from "./ui/select";
-import { Skeleton } from "./ui/skeleton";
-
-
-type OpenAIImageList = { created: string; data: { b64_json: string }[] };
-
-const generateImageSchema = z.object({
- prompt: z.string().min(1, { message: "Prompt is required" }),
- size: z.string(),
-});
-
-export const DallE = () => {
- const [images, setImages] = useState([]);
-
- const { mutate: updateProfilePictureMutation } = useSAToastMutation(async (data: { avatarUrl: string }) => {
- return await updateUserProfileNameAndAvatar(data);
- }, {
- successMessage: "Profile picture updated successfully",
- errorMessage: "Error updating profile picture"
- });
-
- const { mutate: generateImageMutation, isLoading } = useMutation(
- async (data: { prompt: string; size: string }): Promise> => {
- try {
- const { data: response } = await axios.post("/api/generate-image", {
- prompt: data.prompt,
- size: data.size,
- });
- return {
- status: "success",
- data: response as unknown as OpenAIImageList,
- };
- } catch (error) {
- return {
- status: "error",
- message: error.message,
- };
- }
- },
- {
- onSuccess: async (response) => {
- if (response.status === "success") {
- const openAIResponse = response.data as unknown as OpenAIImageList;
- const b64Data = openAIResponse.data[0].b64_json;
-
- try {
- const image = await convertAndUploadOpenAiImage(b64Data);
- if (image.status === "success" && image.data) {
- setImages([image.data]);
- }
- } catch (error) {
- console.error("Error uploading image:", error);
- }
- }
- },
- },
- );
-
- const onSubmit: SubmitHandler<{
- prompt: string;
- size: string;
- }> = (data) => {
- generateImageMutation(data);
- };
-
-
- const { register, handleSubmit, control, formState: { errors } } = useForm({
- defaultValues: {
- prompt: "",
- size: "512x512",
- },
- resolver: zodResolver(generateImageSchema),
- });
-
- return (
-
- {errors.prompt &&
{errors.prompt.message}
}
-
- {!images.length &&
- Your images will be rendered here!
-
}
- {!isLoading ?
- {images.map((image) => (
-
-
-
-
-
-
-
-
-
-
- ))}
-
:
}
-
-
-
- );
-};
diff --git a/src/data/user/chats.ts b/src/data/user/chats.ts
deleted file mode 100644
index ee7c9c3b..00000000
--- a/src/data/user/chats.ts
+++ /dev/null
@@ -1,190 +0,0 @@
-'use server';
-import { supabaseAdminClient } from '@/supabase-clients/admin/supabaseAdminClient';
-import { createSupabaseUserServerActionClient } from '@/supabase-clients/user/createSupabaseUserServerActionClient';
-import { createSupabaseUserServerComponentClient } from '@/supabase-clients/user/createSupabaseUserServerComponentClient';
-import type { SAPayload, SupabaseFileUploadOptions, Table } from '@/types';
-import { serverGetLoggedInUser } from '@/utils/server/serverGetLoggedInUser';
-import type { Message } from 'ai';
-import { nanoid } from 'nanoid';
-import slugify from 'slugify';
-import urlJoin from 'url-join';
-
-export const insertChat = async (
- projectId: string,
- payload: Message[],
- chatId: string,
-): Promise>> => {
- const supabase = createSupabaseUserServerActionClient();
- const user = await serverGetLoggedInUser();
-
- const { data, error } = await supabase
- .from('chats')
- .upsert(
- {
- id: chatId,
- project_id: projectId,
- user_id: user.id,
- payload: JSON.stringify({ messages: payload }),
- },
- { onConflict: 'id' },
- )
- .select('*')
- .single();
-
- if (error) {
- return {
- status: 'error',
- message: error.message,
- };
- }
-
- return {
- status: 'success',
- data: data,
- };
-};
-
-export const getChatById = async (chatId: string): Promise> => {
- const supabase = createSupabaseUserServerComponentClient();
- const { data, error } = await supabase
- .from('chats')
- .select('*')
- .eq('id', chatId)
- .single();
-
- if (error) {
- throw error;
- }
-
- console.log;
-
- return data;
-};
-
-export const deleteChat = async (chatId: string): Promise => {
- const supabase = createSupabaseUserServerActionClient();
- const { error } = await supabase.from('chats').delete().eq('id', chatId);
-
- if (error) {
- throw error;
- }
-};
-
-export const getChats = async (userId: string): Promise[]> => {
- const supabase = createSupabaseUserServerComponentClient();
- const { data, error } = await supabase
- .from('chats')
- .select('*')
- .eq('user_id', userId)
- .order('created_at', { ascending: false });
-
- if (error) {
- throw error;
- }
-
- return data;
-};
-
-export const getChatsHistory = async (
- projectId: string,
- userId: string,
-): Promise[]> => {
- const supabase = createSupabaseUserServerComponentClient();
- const { data, error } = await supabase
- .from('chats')
- .select('*')
- .eq('project_id', projectId)
- .eq('user_id', userId);
-
- if (error) {
- throw error;
- }
-
- return data;
-};
-
-export const convertAndUploadOpenAiImage = async (b64_json: string): Promise> => {
- const byteCharacters = atob(b64_json);
- const byteNumbers = new Array(byteCharacters.length);
- for (let i = 0; i < byteCharacters.length; i++) {
- byteNumbers[i] = byteCharacters.charCodeAt(i);
- }
- const byteArray = new Uint8Array(byteNumbers);
-
- const file = new File([byteArray], nanoid(), { type: 'image/png' });
- const formData = new FormData();
- formData.append('file', file);
-
- const response = await uploadOpenAiImage(formData, file.name, {
- upsert: true,
- });
-
- if (response.status === 'error') {
- return {
- status: 'error',
- message: response.message,
- };
- }
-
-
- if (response.status === "success") {
- return {
- status: "success",
- data: response.data,
- };
- }
-
- return {
- status: "error",
- message: "Unknown error",
- };
-};
-
-export const uploadOpenAiImage = async (
- formData: FormData,
- fileName: string,
- fileOptions?: SupabaseFileUploadOptions | undefined,
-): Promise> => {
- "use server";
- const file = formData.get("file");
- if (!file) {
- return {
- status: "error",
- message: "File is empty",
- };
- }
- const slugifiedFilename = slugify(fileName, {
- lower: true,
- strict: true,
- replacement: "-",
- });
-
- const user = await serverGetLoggedInUser();
- const userId = user.id;
- const userImagesPath = `${userId}/images/${slugifiedFilename}`;
-
- const { data, error } = await supabaseAdminClient.storage
- .from("openai-images")
- .upload(userImagesPath, file, fileOptions);
-
- if (error) {
- return {
- status: "error",
- message: error.message,
- };
- }
-
- const { path } = data;
-
- const filePath = path.split(",")[0];
- const supabaseFileUrl = urlJoin(
- process.env.NEXT_PUBLIC_SUPABASE_URL,
- "/storage/v1/object/public/openai-images",
- filePath,
- );
-
- return {
- status: "success",
- data: supabaseFileUrl,
- };
-};