initial layout
This commit is contained in:
+3346
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,56 @@
|
||||
import createClient from "openapi-react-query"
|
||||
|
||||
import createFetchClient, { Middleware } from "openapi-fetch"
|
||||
|
||||
import { ACCESS_TOKEN_REGEXP, API_URL } from "@shared/lib/constants"
|
||||
|
||||
import { paths } from "./__generated__/openapi.types"
|
||||
|
||||
const isServer = typeof window === "undefined"
|
||||
|
||||
const getAccessTokenFromCookieHeader = (
|
||||
cookieHeader: string | null,
|
||||
): string | undefined => {
|
||||
if (!cookieHeader) return
|
||||
const token = cookieHeader.replace(ACCESS_TOKEN_REGEXP, "$1")
|
||||
return token.length ? token : undefined
|
||||
}
|
||||
|
||||
export const fetchClient = createFetchClient<paths>({
|
||||
baseUrl: API_URL,
|
||||
// credentials: "include",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
})
|
||||
|
||||
const middleware: Middleware = {
|
||||
async onRequest({ request }) {
|
||||
if (request.headers.has("Authorization")) return
|
||||
|
||||
let token: string | undefined
|
||||
if (isServer) {
|
||||
// In middleware/edge runtime there is no `next/headers` request scope.
|
||||
token = getAccessTokenFromCookieHeader(request.headers.get("cookie"))
|
||||
if (!token) {
|
||||
try {
|
||||
const { cookies } = await import("next/headers")
|
||||
token = (await cookies()).get("access_token")?.value
|
||||
} catch {
|
||||
// Not in a request scope (e.g. middleware/edge or build-time).
|
||||
}
|
||||
}
|
||||
} else {
|
||||
token = document.cookie.replace(ACCESS_TOKEN_REGEXP, "$1")
|
||||
}
|
||||
|
||||
if (token?.length) request.headers.set("Authorization", `Bearer ${token}`)
|
||||
},
|
||||
async onError({ error }) {
|
||||
return new Error("Oops, fetch failed", { cause: error })
|
||||
},
|
||||
}
|
||||
fetchClient.use(middleware)
|
||||
|
||||
export const api = createClient(fetchClient)
|
||||
export default api
|
||||
@@ -0,0 +1,29 @@
|
||||
"use server"
|
||||
|
||||
import { fetchClient } from "."
|
||||
|
||||
export const pingServer = async (): Promise<boolean> => {
|
||||
try {
|
||||
await fetchClient.GET("/api/ping/")
|
||||
return true
|
||||
} catch (error) {
|
||||
console.error("Ping server error:", error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
export const verifyToken = async (token: string): Promise<boolean> => {
|
||||
console.log("Verifying token:", token)
|
||||
try {
|
||||
const resp = await fetchClient.GET("/api/users/me/", {
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
})
|
||||
console.log("Verify token response:", resp)
|
||||
return true
|
||||
} catch (error) {
|
||||
console.error("Verify token error:", error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
"use client"
|
||||
|
||||
import { QueryClientProvider as QueryClientProviderTanstack } from "@tanstack/react-query"
|
||||
import { JSX, Suspense } from "react"
|
||||
|
||||
import { queryClient } from "@shared/lib/query_client"
|
||||
|
||||
export const QueryClientProvider = ({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
}): JSX.Element => {
|
||||
return (
|
||||
<QueryClientProviderTanstack client={queryClient}>
|
||||
{children}
|
||||
</QueryClientProviderTanstack>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import Cookies from "js-cookie"
|
||||
|
||||
export const useCookie = (key: string, defaultValue: string | null = null) => {
|
||||
const getCookie = Cookies.get(key) || defaultValue
|
||||
const setCookie = (value: string) => Cookies.set(key, value)
|
||||
const removeCookie = () => Cookies.remove(key)
|
||||
|
||||
return [getCookie, setCookie, removeCookie] as [
|
||||
string | null,
|
||||
(value: string) => void,
|
||||
() => void,
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
export const ACCESS_TOKEN_COOKIE = "access_token"
|
||||
export const REFRESH_TOKEN_COOKIE = "refresh_token"
|
||||
|
||||
export const ACCESS_TOKEN_REGEXP = new RegExp(
|
||||
/(?:(?:^|.*;\s*)access_token\s*=\s*([^;]*).*$)|^.*$/,
|
||||
)
|
||||
|
||||
export const API_URL = process.env.NEXT_PUBLIC_API_URL
|
||||
|
||||
// Paths that can be accessed without authentication and without redirecting to login
|
||||
export const ESSENTIAL_PATHS = [
|
||||
".*/login.*",
|
||||
".*/register.*",
|
||||
".*/reset-password.*",
|
||||
".*/recover.*",
|
||||
]
|
||||
|
||||
// Paths that are excluded from authentication checks
|
||||
export const EXCLUDED_PATHS = [
|
||||
"^/public/.*",
|
||||
"^/_next.*",
|
||||
"^/static.*",
|
||||
"^/fonts.*",
|
||||
".*/api.*",
|
||||
".*/logout.*",
|
||||
".*/confirm-email.*",
|
||||
".*/recover.*",
|
||||
".*/manifest.json.*",
|
||||
".*/android.*",
|
||||
".*/apple.*",
|
||||
".*/favicon.*",
|
||||
".*/workbox.*",
|
||||
".*/sw.js.*",
|
||||
]
|
||||
|
||||
export const ENTRY_PATHS_REGEXP = new RegExp(ESSENTIAL_PATHS.join("|"))
|
||||
export const EXCLUDE_PATHS_REGEXP = new RegExp(EXCLUDED_PATHS.join("|"))
|
||||
@@ -0,0 +1,3 @@
|
||||
import { QueryClient } from "@tanstack/react-query"
|
||||
|
||||
export const queryClient = new QueryClient()
|
||||
@@ -14,6 +14,10 @@
|
||||
|
||||
body {
|
||||
background-color: #f8f8f8;
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
background-color: #121212;
|
||||
}
|
||||
}
|
||||
|
||||
:root {
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export * from "./ui/Alert"
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
import type { AlertProps } from "react-bootstrap/Alert"
|
||||
|
||||
export interface IAlertProps extends AlertProps {}
|
||||
@@ -0,0 +1,13 @@
|
||||
"use client"
|
||||
|
||||
import type { IAlertProps } from "../model/Alert.d"
|
||||
import type { JSX } from "react"
|
||||
|
||||
import { forwardRef } from "react"
|
||||
import BootstrapAlert from "react-bootstrap/Alert"
|
||||
|
||||
export const Alert = forwardRef<HTMLDivElement, IAlertProps>(
|
||||
(props, ref): JSX.Element => <BootstrapAlert ref={ref} {...props} />,
|
||||
)
|
||||
|
||||
Alert.displayName = "Alert"
|
||||
@@ -0,0 +1 @@
|
||||
export * from "./ui/Badge"
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
import type { BadgeProps } from "react-bootstrap/Badge"
|
||||
|
||||
export interface IBadgeProps extends BadgeProps {}
|
||||
@@ -0,0 +1,13 @@
|
||||
"use client"
|
||||
|
||||
import type { IBadgeProps } from "../model/Badge.d"
|
||||
import type { JSX } from "react"
|
||||
|
||||
import { forwardRef } from "react"
|
||||
import BootstrapBadge from "react-bootstrap/Badge"
|
||||
|
||||
export const Badge = forwardRef<HTMLSpanElement, IBadgeProps>(
|
||||
(props, ref): JSX.Element => <BootstrapBadge ref={ref} {...props} />,
|
||||
)
|
||||
|
||||
Badge.displayName = "Badge"
|
||||
Vendored
+1
-3
@@ -1,3 +1 @@
|
||||
export interface IButtonProps {
|
||||
message?: string
|
||||
}
|
||||
export * from "./model/Button.d"
|
||||
|
||||
@@ -1,7 +1 @@
|
||||
import BootstrapButton, { ButtonProps } from "react-bootstrap/Button"
|
||||
|
||||
export const Button = (props: ButtonProps) => (
|
||||
<BootstrapButton variant="primary" {...props}>
|
||||
{props.children}
|
||||
</BootstrapButton>
|
||||
)
|
||||
export * from "./ui/Button"
|
||||
|
||||
@@ -1 +1 @@
|
||||
export * from "./Button"
|
||||
export * from "./ui/Button"
|
||||
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
import type { ButtonProps } from "react-bootstrap/Button"
|
||||
|
||||
export interface IButtonProps extends ButtonProps {}
|
||||
@@ -0,0 +1,13 @@
|
||||
"use client"
|
||||
|
||||
import type { IButtonProps } from "../model/Button.d"
|
||||
import type { JSX } from "react"
|
||||
|
||||
import { forwardRef } from "react"
|
||||
import BootstrapButton from "react-bootstrap/Button"
|
||||
|
||||
export const Button = forwardRef<HTMLButtonElement, IButtonProps>(
|
||||
(props, ref): JSX.Element => <BootstrapButton ref={ref} {...props} />,
|
||||
)
|
||||
|
||||
Button.displayName = "Button"
|
||||
@@ -0,0 +1 @@
|
||||
export * from "./ui/Card"
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
import type { CardProps } from "react-bootstrap/Card"
|
||||
|
||||
export interface ICardProps extends CardProps {}
|
||||
@@ -0,0 +1,13 @@
|
||||
"use client"
|
||||
|
||||
import type { ICardProps } from "../model/Card.d"
|
||||
import type { JSX } from "react"
|
||||
|
||||
import { forwardRef } from "react"
|
||||
import BootstrapCard from "react-bootstrap/Card"
|
||||
|
||||
export const Card = forwardRef<HTMLDivElement, ICardProps>(
|
||||
(props, ref): JSX.Element => <BootstrapCard ref={ref} {...props} />,
|
||||
)
|
||||
|
||||
Card.displayName = "Card"
|
||||
@@ -0,0 +1 @@
|
||||
export * from "./ui/Checkbox"
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
import type { FormCheckProps } from "react-bootstrap/FormCheck"
|
||||
|
||||
export interface ICheckboxProps extends Omit<FormCheckProps, "type"> {}
|
||||
@@ -0,0 +1,15 @@
|
||||
"use client"
|
||||
|
||||
import type { ICheckboxProps } from "../model/Checkbox.d"
|
||||
import type { JSX } from "react"
|
||||
|
||||
import { forwardRef } from "react"
|
||||
import BootstrapFormCheck from "react-bootstrap/FormCheck"
|
||||
|
||||
export const Checkbox = forwardRef<HTMLInputElement, ICheckboxProps>(
|
||||
(props, ref): JSX.Element => (
|
||||
<BootstrapFormCheck ref={ref} type="checkbox" {...props} />
|
||||
),
|
||||
)
|
||||
|
||||
Checkbox.displayName = "Checkbox"
|
||||
@@ -0,0 +1 @@
|
||||
export * from "./ui/Form"
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
import type { FormProps } from "react-bootstrap/Form"
|
||||
|
||||
export interface IFormProps extends FormProps {}
|
||||
@@ -0,0 +1,13 @@
|
||||
"use client"
|
||||
|
||||
import type { IFormProps } from "../model/Form.d"
|
||||
import type { JSX } from "react"
|
||||
|
||||
import { forwardRef } from "react"
|
||||
import BootstrapForm from "react-bootstrap/Form"
|
||||
|
||||
export const Form = forwardRef<HTMLFormElement, IFormProps>(
|
||||
(props, ref): JSX.Element => <BootstrapForm ref={ref} {...props} />,
|
||||
)
|
||||
|
||||
Form.displayName = "Form"
|
||||
@@ -0,0 +1 @@
|
||||
export * from "./ui/Modal"
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
import type { ModalProps } from "react-bootstrap/Modal"
|
||||
|
||||
export interface IModalProps extends ModalProps {}
|
||||
@@ -0,0 +1,13 @@
|
||||
"use client"
|
||||
|
||||
import type { IModalProps } from "../model/Modal.d"
|
||||
import type { JSX } from "react"
|
||||
|
||||
import { forwardRef } from "react"
|
||||
import BootstrapModal from "react-bootstrap/Modal"
|
||||
|
||||
export const Modal = forwardRef<HTMLDivElement, IModalProps>(
|
||||
(props, ref): JSX.Element => <BootstrapModal ref={ref} {...props} />,
|
||||
)
|
||||
|
||||
Modal.displayName = "Modal"
|
||||
@@ -0,0 +1 @@
|
||||
export * from "./ui/Pagination"
|
||||
@@ -0,0 +1,3 @@
|
||||
import type { PaginationProps } from "react-bootstrap/Pagination"
|
||||
|
||||
export interface IPaginationProps extends PaginationProps {}
|
||||
@@ -0,0 +1,13 @@
|
||||
"use client"
|
||||
|
||||
import type { IPaginationProps } from "../model/Pagination.d"
|
||||
import type { JSX } from "react"
|
||||
|
||||
import { forwardRef } from "react"
|
||||
import BootstrapPagination from "react-bootstrap/Pagination"
|
||||
|
||||
export const Pagination = forwardRef<HTMLUListElement, IPaginationProps>(
|
||||
(props, ref): JSX.Element => <BootstrapPagination ref={ref} {...props} />,
|
||||
)
|
||||
|
||||
Pagination.displayName = "Pagination"
|
||||
@@ -0,0 +1 @@
|
||||
export * from "./ui/Radio"
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
import type { FormCheckProps } from "react-bootstrap/FormCheck"
|
||||
|
||||
export interface IRadioProps extends Omit<FormCheckProps, "type"> {}
|
||||
@@ -0,0 +1,15 @@
|
||||
"use client"
|
||||
|
||||
import type { IRadioProps } from "../model/Radio.d"
|
||||
import type { JSX } from "react"
|
||||
|
||||
import { forwardRef } from "react"
|
||||
import BootstrapFormCheck from "react-bootstrap/FormCheck"
|
||||
|
||||
export const Radio = forwardRef<HTMLInputElement, IRadioProps>(
|
||||
(props, ref): JSX.Element => (
|
||||
<BootstrapFormCheck ref={ref} type="radio" {...props} />
|
||||
),
|
||||
)
|
||||
|
||||
Radio.displayName = "Radio"
|
||||
@@ -0,0 +1 @@
|
||||
export * from "./ui/Select"
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
import type { FormSelectProps } from "react-bootstrap/FormSelect"
|
||||
|
||||
export interface ISelectProps extends FormSelectProps {}
|
||||
@@ -0,0 +1,13 @@
|
||||
"use client"
|
||||
|
||||
import type { ISelectProps } from "../model/Select.d"
|
||||
import type { JSX } from "react"
|
||||
|
||||
import { forwardRef } from "react"
|
||||
import BootstrapFormSelect from "react-bootstrap/FormSelect"
|
||||
|
||||
export const Select = forwardRef<HTMLSelectElement, ISelectProps>(
|
||||
(props, ref): JSX.Element => <BootstrapFormSelect ref={ref} {...props} />,
|
||||
)
|
||||
|
||||
Select.displayName = "Select"
|
||||
@@ -0,0 +1 @@
|
||||
export * from "./ui/Table"
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
import type { TableProps } from "react-bootstrap/Table"
|
||||
|
||||
export interface ITableProps extends TableProps {}
|
||||
@@ -0,0 +1,13 @@
|
||||
"use client"
|
||||
|
||||
import type { ITableProps } from "../model/Table.d"
|
||||
import type { JSX } from "react"
|
||||
|
||||
import { forwardRef } from "react"
|
||||
import BootstrapTable from "react-bootstrap/Table"
|
||||
|
||||
export const Table = forwardRef<HTMLTableElement, ITableProps>(
|
||||
(props, ref): JSX.Element => <BootstrapTable ref={ref} {...props} />,
|
||||
)
|
||||
|
||||
Table.displayName = "Table"
|
||||
@@ -0,0 +1 @@
|
||||
export * from "./ui/Tabs"
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
import type { TabsProps } from "react-bootstrap/Tabs"
|
||||
|
||||
export interface ITabsProps extends TabsProps {}
|
||||
@@ -0,0 +1,18 @@
|
||||
"use client"
|
||||
|
||||
import type { ITabsProps } from "../model/Tabs.d"
|
||||
import type { ForwardRefExoticComponent, JSX, RefAttributes } from "react"
|
||||
|
||||
import { forwardRef } from "react"
|
||||
import BootstrapTabs from "react-bootstrap/Tabs"
|
||||
|
||||
const BootstrapTabsWithRef =
|
||||
BootstrapTabs as unknown as ForwardRefExoticComponent<
|
||||
ITabsProps & RefAttributes<HTMLDivElement>
|
||||
>
|
||||
|
||||
export const Tabs = forwardRef<HTMLDivElement, ITabsProps>(
|
||||
(props, ref): JSX.Element => <BootstrapTabsWithRef ref={ref} {...props} />,
|
||||
)
|
||||
|
||||
Tabs.displayName = "Tabs"
|
||||
@@ -0,0 +1 @@
|
||||
export * from "./ui/TextField"
|
||||
@@ -0,0 +1,7 @@
|
||||
import type { FormControlProps } from "react-bootstrap/FormControl"
|
||||
|
||||
export interface ITextFieldProps extends FormControlProps {
|
||||
id: string
|
||||
label?: string
|
||||
undertitle?: string
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
"use client"
|
||||
|
||||
import type { ITextFieldProps } from "../model/TextField.d"
|
||||
import type { JSX } from "react"
|
||||
|
||||
import React, { forwardRef } from "react"
|
||||
import BootstrapForm from "react-bootstrap/Form"
|
||||
|
||||
export const TextField = forwardRef<HTMLInputElement, ITextFieldProps>(
|
||||
({ id, label, undertitle, ...props }, ref): JSX.Element => (
|
||||
<React.Fragment>
|
||||
{label && <BootstrapForm.Label htmlFor={id}>{label}</BootstrapForm.Label>}
|
||||
<BootstrapForm.Control
|
||||
id={id}
|
||||
ref={ref}
|
||||
{...props}
|
||||
aria-describedby={`${id}-undertitle`}
|
||||
/>
|
||||
{undertitle && (
|
||||
<BootstrapForm.Text id={`${id}-undertitle`} muted>
|
||||
{undertitle}
|
||||
</BootstrapForm.Text>
|
||||
)}
|
||||
</React.Fragment>
|
||||
),
|
||||
)
|
||||
|
||||
TextField.displayName = "TextField"
|
||||
@@ -1 +1,13 @@
|
||||
export * from "./Alert"
|
||||
export * from "./Badge"
|
||||
export * from "./Button"
|
||||
export * from "./Card"
|
||||
export * from "./Checkbox"
|
||||
export * from "./Form"
|
||||
export * from "./TextField"
|
||||
export * from "./Modal"
|
||||
export * from "./Pagination"
|
||||
export * from "./Radio"
|
||||
export * from "./Select"
|
||||
export * from "./Table"
|
||||
export * from "./Tabs"
|
||||
|
||||
Reference in New Issue
Block a user