-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
412 additions
and
177 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
{ | ||
"$schema": "https://ui.shadcn.com/schema.json", | ||
"style": "default", | ||
"rsc": true, | ||
"tsx": true, | ||
"tailwind": { | ||
"config": "tailwind.config.ts", | ||
"css": "src/styles/globals.css", | ||
"baseColor": "slate", | ||
"cssVariables": true, | ||
"prefix": "" | ||
}, | ||
"aliases": { | ||
"components": "@/components", | ||
"utils": "@/lib/utils" | ||
} | ||
} |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import { type ClassValue, clsx } from "clsx" | ||
import { twMerge } from "tailwind-merge" | ||
|
||
export function cn(...inputs: ClassValue[]) { | ||
return twMerge(clsx(inputs)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
export interface User { | ||
id: string; | ||
name: string; | ||
email: string; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
/** | ||
* Represents an HTTP error. | ||
*/ | ||
export class HttpError extends Error { | ||
/** | ||
* Constructs a new instance of the HttpError class. | ||
* @param message - Optional error message. | ||
*/ | ||
constructor(message?: string) { | ||
super(message); | ||
this.name = this.constructor.name; | ||
} | ||
} | ||
|
||
/** | ||
* Status code: 400 | ||
*/ | ||
export class BadRequestError extends HttpError {} | ||
|
||
/** | ||
* Status code: 401 | ||
*/ | ||
export class UnauthorizedError extends HttpError {} | ||
|
||
/** | ||
* Status code: 403 | ||
*/ | ||
export class ForbiddenError extends HttpError {} | ||
|
||
/** | ||
* Status code: 404 | ||
*/ | ||
export class NotFoundError extends HttpError {} | ||
|
||
/** | ||
* Status code: 409 | ||
*/ | ||
export class ConflictError extends HttpError {} | ||
|
||
// Map HTTP status code to error class | ||
export const statusCodeErrorMap: { | ||
[key: number]: new (message?: string) => HttpError; | ||
} = { | ||
400: BadRequestError, | ||
401: UnauthorizedError, | ||
403: ForbiddenError, | ||
404: NotFoundError, | ||
409: ConflictError, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
import { cookies } from "next/headers"; | ||
import { statusCodeErrorMap } from "../errors/httpErrors"; | ||
|
||
/** | ||
* Fetches data from the server. | ||
* @param input - The URL to fetch. | ||
* @param init - The optional RequestInit object for additional configuration. | ||
* @param isServerRequest - Whether the request is made from the server. | ||
* @returns A Promise that resolves to the Response object representing the fetched data. | ||
* @throws Error if the base URL is not set, if fetching data fails, or if an HTTP error occurs. | ||
*/ | ||
export async function fetchData( | ||
input: RequestInfo, | ||
init: RequestInit = {}, | ||
isServerRequest = false | ||
) { | ||
const baseURL = process.env.NEXT_PUBLIC_BASE_URL; | ||
|
||
if (!baseURL) { | ||
throw new Error("Base URL is not set."); | ||
} | ||
|
||
// Add json header if body is present | ||
if (init.body) { | ||
init.headers = { | ||
...init.headers, | ||
"Content-Type": "application/json", | ||
}; | ||
} | ||
|
||
// Add cookies if request from server | ||
if (isServerRequest) { | ||
init.headers = { | ||
...init.headers, | ||
Cookie: cookies().toString(), | ||
}; | ||
} | ||
|
||
// Fetch data from the server | ||
let response; | ||
try { | ||
response = await fetch(baseURL + input, init); | ||
} catch (error) { | ||
throw new Error("Failed to fetch data"); | ||
} | ||
|
||
// Check HTTP errors | ||
if (!response.ok) { | ||
const body = await response.json(); | ||
const errorMessage = body.error || "Unknown error occurred."; | ||
|
||
// Map HTTP status code to error class | ||
if (response.status in statusCodeErrorMap) { | ||
throw new statusCodeErrorMap[response.status](errorMessage); | ||
} | ||
|
||
// Generic error | ||
throw new Error( | ||
"Something went wrong: " + response.status + " : " + errorMessage | ||
); | ||
} | ||
|
||
return response; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import { User } from "@/models/user"; | ||
import { fetchData } from "./helpers/apiHelper"; | ||
|
||
interface LoginBody { | ||
email: string; | ||
password: string; | ||
} | ||
|
||
interface RegisterBody { | ||
name: string; | ||
email: string; | ||
password: string; | ||
userType: "tenant" | "landlord"; | ||
} | ||
|
||
export const loginClient = async (body: LoginBody): Promise<User> => { | ||
const response = await fetchData("/users/login", { | ||
method: "POST", | ||
body: JSON.stringify(body), | ||
}); | ||
return await response.json(); | ||
}; | ||
|
||
export const registerClient = async (body: RegisterBody): Promise<User> => { | ||
const response = await fetchData("/users/signup", { | ||
method: "POST", | ||
body: JSON.stringify(body), | ||
}); | ||
return await response.json(); | ||
}; | ||
|
||
export const logoutClient = async () => { | ||
await fetchData("/users/logout", { | ||
method: "POST", | ||
}); | ||
}; | ||
|
||
export const getCurrentUserServer = async (): Promise<User> => { | ||
const response = await fetchData("/users/me", {}, true); | ||
return await response.json(); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,35 +1,76 @@ | ||
@tailwind base; | ||
@tailwind components; | ||
@tailwind utilities; | ||
|
||
@layer base { | ||
:root { | ||
--background: 0 0% 100%; | ||
--foreground: 222.2 84% 4.9%; | ||
|
||
/* Tailwind */ | ||
@layer utilities { | ||
.text-balance { | ||
text-wrap: balance; | ||
} | ||
} | ||
--card: 0 0% 100%; | ||
--card-foreground: 222.2 84% 4.9%; | ||
|
||
--popover: 0 0% 100%; | ||
--popover-foreground: 222.2 84% 4.9%; | ||
|
||
--primary: 222.2 47.4% 11.2%; | ||
--primary-foreground: 210 40% 98%; | ||
|
||
--secondary: 210 40% 96.1%; | ||
--secondary-foreground: 222.2 47.4% 11.2%; | ||
|
||
--muted: 210 40% 96.1%; | ||
--muted-foreground: 215.4 16.3% 46.9%; | ||
|
||
--accent: 210 40% 96.1%; | ||
--accent-foreground: 222.2 47.4% 11.2%; | ||
|
||
--destructive: 0 84.2% 60.2%; | ||
--destructive-foreground: 210 40% 98%; | ||
|
||
/* Root */ | ||
@media (prefers-color-scheme: light) { | ||
:root { | ||
--text: #011307; | ||
--background: #f8fdfa; | ||
--primary: #2fb673; | ||
--secondary: #83d6fa; | ||
--accent: #4994f8; | ||
} | ||
} | ||
@media (prefers-color-scheme: dark) { | ||
:root { | ||
--text: #ecfef2; | ||
--background: #020805; | ||
--primary: #49d08c; | ||
--secondary: #05577a; | ||
--accent: #0753b6; | ||
} | ||
--border: 214.3 31.8% 91.4%; | ||
--input: 214.3 31.8% 91.4%; | ||
--ring: 222.2 84% 4.9%; | ||
|
||
--radius: 0.5rem; | ||
} | ||
|
||
.dark { | ||
--background: 222.2 84% 4.9%; | ||
--foreground: 210 40% 98%; | ||
|
||
--card: 222.2 84% 4.9%; | ||
--card-foreground: 210 40% 98%; | ||
|
||
--popover: 222.2 84% 4.9%; | ||
--popover-foreground: 210 40% 98%; | ||
|
||
--primary: 210 40% 98%; | ||
--primary-foreground: 222.2 47.4% 11.2%; | ||
|
||
--secondary: 217.2 32.6% 17.5%; | ||
--secondary-foreground: 210 40% 98%; | ||
|
||
--muted: 217.2 32.6% 17.5%; | ||
--muted-foreground: 215 20.2% 65.1%; | ||
|
||
--accent: 217.2 32.6% 17.5%; | ||
--accent-foreground: 210 40% 98%; | ||
|
||
--destructive: 0 62.8% 30.6%; | ||
--destructive-foreground: 210 40% 98%; | ||
|
||
--border: 217.2 32.6% 17.5%; | ||
--input: 217.2 32.6% 17.5%; | ||
--ring: 212.7 26.8% 83.9%; | ||
} | ||
} | ||
|
||
body { | ||
color: --text; | ||
background: --background; | ||
|
||
@layer base { | ||
* { | ||
@apply border-border; | ||
} | ||
body { | ||
@apply bg-background text-foreground; | ||
} | ||
} |
Oops, something went wrong.