From a72277222f9dd21565a048c9e5d3c313fac4f2b6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Yusuf=20Akg=C3=BCl?= <s86116@bht-berlin.de>
Date: Mon, 10 Jul 2023 01:53:00 +0200
Subject: [PATCH] added better type safety and some cleanup

---
 .../(gaming)/games/[gameid]/page.tsx          | 10 ++-
 app/api/gweets/route.ts                       |  2 +-
 .../create-gweet/components/create-gweet.tsx  | 20 ++---
 components/icons.tsx                          |  6 +-
 components/nav-sidebar.tsx                    |  4 +-
 components/profile/api/update-profile.ts      |  6 +-
 .../profile/components/edit-profile-modal.tsx |  2 +-
 jest.config.mjs                               | 14 ++--
 package-lock.json                             |  1 +
 package.json                                  |  8 +-
 tsconfig.json                                 | 79 ++++++++++---------
 11 files changed, 79 insertions(+), 73 deletions(-)

diff --git a/app/(content)/(gaming)/games/[gameid]/page.tsx b/app/(content)/(gaming)/games/[gameid]/page.tsx
index f344c53..cfded75 100644
--- a/app/(content)/(gaming)/games/[gameid]/page.tsx
+++ b/app/(content)/(gaming)/games/[gameid]/page.tsx
@@ -2,6 +2,7 @@ import AddGameDropdown from "@/components/add-game-dropdown"
 import AddGameToFavList from "@/components/addGameToFavList"
 import { BackHeader } from "@/components/back-header"
 import { GlobalLayout } from "@/components/global-layout"
+import { TryAgain } from "@/components/try-again"
 import { AspectRatio } from "@/components/ui/aspect-ratio"
 import { Button } from "@/components/ui/button"
 import { Card } from "@/components/ui/card"
@@ -15,6 +16,9 @@ import Image from "next/image"
 // renders a single game detail page
 export default async function GameDetail({ params }: { params: { gameid: string } }) {
     const data: IGame[] = await getGame(parseInt(params.gameid))
+
+    if (!data[0]) return <TryAgain />
+
     // TODO put to backend
     const date = formatDate(data[0].first_release_date * 1000)
 
@@ -26,7 +30,7 @@ export default async function GameDetail({ params }: { params: { gameid: string
     })
 
     const companies = data[0].involved_companies.map((company) => {
-        if (company !== data[0].involved_companies[0]) {
+        if (company !== data[0]?.involved_companies[0]) {
             return `, ${company.company.name}`
         }
         return company.company.name
@@ -45,7 +49,7 @@ export default async function GameDetail({ params }: { params: { gameid: string
                     <div className="h-64 overflow-hidden">
                         <AspectRatio ratio={889 / 500}>
                             <Image
-                                src={data[0].screenshots[0].url}
+                                src={data[0].screenshots[0]?.url ?? ""}
                                 alt={data[0].name}
                                 fill
                                 priority
@@ -113,7 +117,7 @@ export default async function GameDetail({ params }: { params: { gameid: string
                                 <Card key={i} className="aspect-[264/374] relative block">
                                     <Image
                                         src={screenshot.url}
-                                        alt={data[0].name}
+                                        alt={data[0]?.name ?? ""}
                                         fill
                                         priority
                                         className="object-cover rounded-lg" />
diff --git a/app/api/gweets/route.ts b/app/api/gweets/route.ts
index 036b8d3..fd57494 100644
--- a/app/api/gweets/route.ts
+++ b/app/api/gweets/route.ts
@@ -160,7 +160,7 @@ export async function GET(request: Request) {
             },
         })
 
-        const nextId = gweets.length < take ? undefined : gweets[gweets.length - 1].id
+        const nextId = gweets.length < take ? undefined : gweets[gweets.length - 1]?.id
 
         return NextResponse.json({ gweets, nextId }, { status: 200 })
     } catch (error) {
diff --git a/components/create-gweet/components/create-gweet.tsx b/components/create-gweet/components/create-gweet.tsx
index 491c57b..2788484 100644
--- a/components/create-gweet/components/create-gweet.tsx
+++ b/components/create-gweet/components/create-gweet.tsx
@@ -114,7 +114,7 @@ export const CreateGweet = ({
             const err = error as z.ZodError
             return toast({
                 variant: "destructive",
-                description: err.issues[0].message,
+                description: err.issues[0]?.message,
             })
         }
 
@@ -133,16 +133,18 @@ export const CreateGweet = ({
                 const filePath = files[i]
 
                 const reader = new FileReader()
-                reader.readAsDataURL(filePath)
+                if (filePath) {
+                    reader.readAsDataURL(filePath)
 
-                reader.onload = () => {
-                    newImages.push({
-                        url: reader.result,
-                        file: filePath,
-                    })
+                    reader.onload = () => {
+                        newImages.push({
+                            url: reader.result,
+                            file: filePath,
+                        })
 
-                    if (newImages.length === files.length) {
-                        setChosenImages([...chosenImages, ...newImages])
+                        if (newImages.length === files.length) {
+                            setChosenImages([...chosenImages, ...newImages])
+                        }
                     }
                 }
             }
diff --git a/components/icons.tsx b/components/icons.tsx
index 4c26e62..e21aac5 100644
--- a/components/icons.tsx
+++ b/components/icons.tsx
@@ -40,13 +40,9 @@ import {
     type Icon as LucideIcon,
 } from "lucide-react"
 
-export type IconsType = {
-    [key: string]: LucideIcon
-}
-
 export type Icon = LucideIcon
 
-export const Icons: IconsType = {
+export const Icons = {
     logo: ({ ...props }: LucideProps) => (
         <svg width="36" height="36" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
             <path d="M20.5603 25.7199V34C20.5603 35.1046 19.6649 36 18.5603 36H12.2803C11.1757 36 10.2803 35.1046 10.2803 34V25.7199L15.5127 20.5L20.5603 25.7199Z" fill="#16161A" />
diff --git a/components/nav-sidebar.tsx b/components/nav-sidebar.tsx
index b8f579a..382419d 100644
--- a/components/nav-sidebar.tsx
+++ b/components/nav-sidebar.tsx
@@ -1,6 +1,6 @@
 "use client"
 
-import { Icons, IconsType } from "@/components/icons"
+import { Icons } from "@/components/icons"
 import { buttonVariants } from "@/components/ui/button"
 import { cn } from "@/lib/utils"
 import { SidebarNavItem } from "@/types"
@@ -21,7 +21,7 @@ export const Sidebar = ({ items, user }: { items: SidebarNavItem[], user: User |
     return (
         <nav className="grid items-start gap-2">
             {visibleItems.map((item, index) => {
-                const Icon = Icons[item.icon as keyof IconsType || "arrowRight"]
+                const Icon = Icons[item.icon || "arrowRight"]
                 if (item.title === "My Profile") {
                     item.href = `/${user?.username}`
                 }
diff --git a/components/profile/api/update-profile.ts b/components/profile/api/update-profile.ts
index 3154005..7fb9c7a 100644
--- a/components/profile/api/update-profile.ts
+++ b/components/profile/api/update-profile.ts
@@ -15,11 +15,13 @@ export const updateProfile = async ({
         let imagefileprops: { fileUrl: string; fileKey: string } = { fileUrl: '', fileKey: '' }
 
         if (profile.banner.file) {
-            [bannerfileprops] = await uploadFiles({ files: [profile.banner.file], endpoint: 'mediaUploader' })
+            [bannerfileprops = bannerfileprops ?? { fileUrl: '', fileKey: '' }] =
+                await uploadFiles({ files: [profile.banner.file], endpoint: 'mediaUploader' })
         }
 
         if (profile.image.file) {
-            [imagefileprops] = await uploadFiles({ files: [profile.image.file], endpoint: 'imageUploader' })
+            [imagefileprops = imagefileprops ?? { fileUrl: '', fileKey: '' }] =
+                await uploadFiles({ files: [profile.image.file], endpoint: 'imageUploader' })
         }
 
         const data = await fetch(`/api/users/${userId}`, {
diff --git a/components/profile/components/edit-profile-modal.tsx b/components/profile/components/edit-profile-modal.tsx
index d77e188..6039e78 100644
--- a/components/profile/components/edit-profile-modal.tsx
+++ b/components/profile/components/edit-profile-modal.tsx
@@ -127,7 +127,7 @@ export const EditProfileModal = ({ user }: { user: IUser }) => {
             setImageErrorMessage("")
         } catch (error: any) {
             const err = error as z.ZodError
-            setImageErrorMessage(err.issues[0].message)
+            setImageErrorMessage(err.issues[0]?.message || "Invalid image")
             return
         }
 
diff --git a/jest.config.mjs b/jest.config.mjs
index 7079cf9..4e300ff 100644
--- a/jest.config.mjs
+++ b/jest.config.mjs
@@ -1,4 +1,4 @@
-import nextJest from 'next/jest.js';
+import nextJest from 'next/jest.js'
 
 // Provide the path to your Next.js app to load next.config.js and .env files in your test environment
 const createJestConfig = nextJest({ dir: './' })
@@ -9,14 +9,14 @@ const clientTestConfig = {
     testPathIgnorePatterns: ["<rootDir>/node_modules/", "<rootDir>/__tests__/api/"],
     setupFilesAfterEnv: ["<rootDir>/jest.setup.js"],
     testEnvironment: "jest-environment-jsdom",
-};
+}
 
 // Custom server config to be passed to Jest.
 const serverTestConfig = {
     displayName: "server",
     testMatch: ["**/__tests__/api/**/*.[jt]s?(x)", "**/?(*.)+(spec|test).[jt]s?(x)"],
     testEnvironment: "jest-environment-node",
-};
+}
 
 // Add any custom config to be passed to Jest
 /** @type {import('jest').Config} */
@@ -26,8 +26,8 @@ const config = {
     collectCoverageFrom: [
         '**/app/api/**',
         '!**/node_modules/**',
-        '!**/vendor/**',
-      ]
-};
+        '!**/.*/**',
+    ],
+}
 
-export default config;
\ No newline at end of file
+export default config
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index bb235f4..9ac6fb7 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -7,6 +7,7 @@
         "": {
             "name": "project_ss23_gameunity",
             "version": "0.2.0",
+            "hasInstallScript": true,
             "dependencies": {
                 "@auth/prisma-adapter": "^1.0.0",
                 "@hookform/resolvers": "^3.1.1",
diff --git a/package.json b/package.json
index eb931eb..453dfa6 100644
--- a/package.json
+++ b/package.json
@@ -8,10 +8,10 @@
         "build": "next build",
         "start": "next start",
         "lint": "next lint",
-        "preview": "prisma generate && next build && next start",
+        "test": "jest --coverage",
+        "preview": "next build && next start",
         "vercel-build": "prisma generate && prisma migrate deploy && next build",
-        "test": "jest",
-        "coverage": "jest --coverage"
+        "postinstall": "prisma generate"
     },
     "dependencies": {
         "@auth/prisma-adapter": "^1.0.0",
@@ -79,4 +79,4 @@
         "semver": "^7.5.3",
         "optionator": "^0.9.3"
     }
-}
+}
\ No newline at end of file
diff --git a/tsconfig.json b/tsconfig.json
index 88c4dba..5fc1a97 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,42 +1,43 @@
 {
-  "compilerOptions": {
-    "target": "es5",
-    "lib": [
-      "dom",
-      "dom.iterable",
-      "esnext"
-    ],
-    "allowJs": true,
-    "skipLibCheck": true,
-    "strict": true,
-    "forceConsistentCasingInFileNames": true,
-    "noEmit": true,
-    "esModuleInterop": true,
-    "module": "esnext",
-    "moduleResolution": "node",
-    "resolveJsonModule": true,
-    "isolatedModules": true,
-    "jsx": "preserve",
-    "incremental": true,
-    "plugins": [
-      {
-        "name": "next"
-      }
-    ],
-    "paths": {
-      "@/*": [
-        "./*"
-      ]
+    "compilerOptions": {
+        "target": "es5",
+        "lib": [
+            "dom",
+            "dom.iterable",
+            "esnext"
+        ],
+        "allowJs": true,
+        "skipLibCheck": true,
+        "strict": true,
+        "noUncheckedIndexedAccess": true,
+        "forceConsistentCasingInFileNames": true,
+        "noEmit": true,
+        "esModuleInterop": true,
+        "module": "esnext",
+        "moduleResolution": "node",
+        "resolveJsonModule": true,
+        "isolatedModules": true,
+        "jsx": "preserve",
+        "incremental": true,
+        "plugins": [
+            {
+                "name": "next"
+            }
+        ],
+        "paths": {
+            "@/*": [
+                "./*"
+            ]
+        },
+        "strictNullChecks": true
     },
-    "strictNullChecks": true
-  },
-  "include": [
-    "next-env.d.ts",
-    "**/*.ts",
-    "**/*.tsx",
-    ".next/types/**/*.ts"
-  ],
-  "exclude": [
-    "node_modules"
-  ]
+    "include": [
+        "next-env.d.ts",
+        "**/*.ts",
+        "**/*.tsx",
+        ".next/types/**/*.ts"
+    ],
+    "exclude": [
+        "node_modules"
+    ]
 }
\ No newline at end of file
-- 
GitLab