Migrated from React to NextJS
This commit is contained in:
@ -1,84 +0,0 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
env: {
|
||||
node: true,
|
||||
es6: true,
|
||||
},
|
||||
parserOptions: { ecmaVersion: 8, sourceType: "module" },
|
||||
ignorePatterns: ["node_modules/*"],
|
||||
extends: ["eslint:recommended"],
|
||||
overrides: [
|
||||
{
|
||||
files: ["**/*.ts", "**/*.tsx"],
|
||||
parser: "@typescript-eslint/parser",
|
||||
settings: {
|
||||
react: { version: "detect" },
|
||||
"import/resolver": {
|
||||
typescript: {},
|
||||
},
|
||||
},
|
||||
env: {
|
||||
browser: true,
|
||||
node: true,
|
||||
es6: true,
|
||||
},
|
||||
extends: [
|
||||
"eslint:recommended",
|
||||
"plugin:import/errors",
|
||||
"plugin:import/warnings",
|
||||
"plugin:import/typescript",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:react/recommended",
|
||||
"plugin:react-hooks/recommended",
|
||||
"plugin:jsx-a11y/recommended",
|
||||
"plugin:prettier/recommended",
|
||||
"plugin:testing-library/react",
|
||||
"plugin:jest-dom/recommended",
|
||||
],
|
||||
rules: {
|
||||
"react/display-name": "off",
|
||||
"no-restricted-imports": [
|
||||
"error",
|
||||
{
|
||||
patterns: ["@/features/*/*"],
|
||||
},
|
||||
],
|
||||
"linebreak-style": ["error", "unix"],
|
||||
"react/prop-types": "off",
|
||||
|
||||
"import/order": [
|
||||
"error",
|
||||
{
|
||||
groups: [
|
||||
"builtin",
|
||||
"external",
|
||||
"internal",
|
||||
"parent",
|
||||
"sibling",
|
||||
"index",
|
||||
"object",
|
||||
],
|
||||
"newlines-between": "always",
|
||||
alphabetize: { order: "asc", caseInsensitive: true },
|
||||
},
|
||||
],
|
||||
"import/default": "off",
|
||||
"import/no-named-as-default-member": "off",
|
||||
"import/no-named-as-default": "off",
|
||||
|
||||
"react/react-in-jsx-scope": "off",
|
||||
|
||||
"jsx-a11y/anchor-is-valid": "off",
|
||||
|
||||
"@typescript-eslint/no-unused-vars": ["error"],
|
||||
|
||||
"@typescript-eslint/explicit-function-return-type": ["off"],
|
||||
"@typescript-eslint/explicit-module-boundary-types": ["off"],
|
||||
"@typescript-eslint/no-empty-function": ["off"],
|
||||
"@typescript-eslint/no-explicit-any": ["off"],
|
||||
|
||||
"prettier/prettier": ["error", {}, { usePrettierrc: true }],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
3
client/.eslintrc.json
Normal file
3
client/.eslintrc.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"extends": "next/core-web-vitals"
|
||||
}
|
54
client/.gitignore
vendored
54
client/.gitignore
vendored
@ -1,24 +1,40 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# next.js
|
||||
/.next/
|
||||
/out/
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
# local env files
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
# vercel
|
||||
.vercel
|
||||
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
|
||||
cypress/screenshots
|
||||
cypress/videos
|
||||
|
54
client/.package.json
Normal file
54
client/.package.json
Normal file
@ -0,0 +1,54 @@
|
||||
{
|
||||
"name": "client",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc && vite build --outDir ../dist",
|
||||
"preview": "vite preview",
|
||||
"lint": "eslint --fix --ext .js,.ts,.tsx ./src --ignore-path .gitignore",
|
||||
"prettier": "prettier --ignore-path .gitignore --write \"**/*.+(js|json|ts|tsx)\"",
|
||||
"cypress": "cypress"
|
||||
},
|
||||
"dependencies": {
|
||||
"@headlessui/react": "^1.7.2",
|
||||
"@heroicons/react": "^2.0.11",
|
||||
"@hookform/resolvers": "^2.9.8",
|
||||
"@ory/client": "0.2.0-alpha.48",
|
||||
"axios": "^0.27.2",
|
||||
"clsx": "^1.2.1",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-hook-form": "^7.36.1",
|
||||
"react-router-dom": "^6.4.1",
|
||||
"tailwind-scrollbar": "^2.0.1",
|
||||
"zod": "^3.19.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@testing-library/cypress": "^8.0.3",
|
||||
"@types/react": "^18.0.17",
|
||||
"@types/react-dom": "^18.0.6",
|
||||
"@typescript-eslint/eslint-plugin": "^5.39.0",
|
||||
"@typescript-eslint/parser": "^5.39.0",
|
||||
"@vitejs/plugin-react": "^2.1.0",
|
||||
"autoprefixer": "^10.4.12",
|
||||
"cypress": "^10.9.0",
|
||||
"eslint": "^8.24.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-import-resolver-typescript": "^3.5.1",
|
||||
"eslint-plugin-cypress": "^2.12.1",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-jest-dom": "^4.0.2",
|
||||
"eslint-plugin-jsx-a11y": "^6.6.1",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"eslint-plugin-react": "^7.31.8",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"eslint-plugin-testing-library": "^5.7.2",
|
||||
"postcss": "^8.4.16",
|
||||
"prettier": "^2.7.1",
|
||||
"tailwindcss": "^3.1.8",
|
||||
"typescript": "^4.6.4",
|
||||
"vite": "^3.1.0"
|
||||
}
|
||||
}
|
6
client/.prettierrc.js
Normal file
6
client/.prettierrc.js
Normal file
@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
...require('ory-prettier-styles'),
|
||||
importOrder: ['^\\.\\./(?!.*\\.[a-z]+$)(.*)$', '^\\./(?!.*\\.[a-z]+$)(.*)$'],
|
||||
importOrderSeparation: true,
|
||||
experimentalBabelParserPluginsList: ['jsx', 'typescript']
|
||||
}
|
@ -1,12 +1,13 @@
|
||||
import { ArrowLeftIcon } from '@heroicons/react/24/outline';
|
||||
import { Outlet, NavLink } from 'react-router-dom';
|
||||
|
||||
import Button from '../components/Button';
|
||||
import NavBar from '../components/NavBar';
|
||||
import SideNavChannel from '../components/SideNavChannel';
|
||||
import Button from './Button';
|
||||
import NavBar from './NavBar';
|
||||
import SideNavChannel from './SideNavChannel';
|
||||
import streamData from '../placeholder/GetStreams';
|
||||
import { NextPage } from 'next';
|
||||
import Link from 'next/link';
|
||||
|
||||
function BrowseLayout() {
|
||||
const BrowseLayout: NextPage = ({children}) => {
|
||||
return (
|
||||
<div className="font-inter flex flex-col h-screen text-gray-100">
|
||||
<NavBar />
|
||||
@ -21,14 +22,14 @@ function BrowseLayout() {
|
||||
<ul className="flex-1 overflow-scrollbar">
|
||||
{streamData.data.map((stream) => (
|
||||
<li key={stream.id}>
|
||||
<NavLink to={`/${stream.user_login}`}>
|
||||
<Link href={`/${stream.user_login}`} passHref={true}>
|
||||
<SideNavChannel stream={stream} />
|
||||
</NavLink>
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
<Outlet />
|
||||
{children}
|
||||
</main>
|
||||
</div>
|
||||
);
|
17
client/components/InlineLink.tsx
Normal file
17
client/components/InlineLink.tsx
Normal file
@ -0,0 +1,17 @@
|
||||
import clsx from 'clsx';
|
||||
import { FC } from 'react';
|
||||
import Link from 'next/link';
|
||||
|
||||
export interface InlineLinkProps extends React.ComponentPropsWithoutRef<'span'> {
|
||||
to: string;
|
||||
}
|
||||
|
||||
const InlineLink: FC<InlineLinkProps> = ({ to, className, ...rest }) => {
|
||||
return (
|
||||
<Link href={to} passHref={true}>
|
||||
<span className={clsx('text-violet-400 cursor-pointer text-sm', className)} {...rest} />
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
||||
export default InlineLink;
|
@ -1,8 +1,5 @@
|
||||
import { Dialog, Tab } from '@headlessui/react';
|
||||
import { FC } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
|
||||
import logo from '../assets/images/logo.png';
|
||||
import { FC, useEffect, useRef } from 'react';
|
||||
|
||||
import LoginForm from './LoginForm';
|
||||
import LoginModalTab from './LoginModalTab';
|
||||
@ -15,13 +12,13 @@ export interface LoginModelProps {
|
||||
}
|
||||
|
||||
const LoginModal: FC<LoginModelProps> = ({ defaultPage, isOpen, onClose }) => {
|
||||
return createPortal(
|
||||
return (
|
||||
<Dialog open={isOpen} onClose={onClose} className="relative z-50">
|
||||
<div className="bg-black/80 fixed inset-0 flex items-center justify-center">
|
||||
<Dialog.Panel className="bg-zinc-900 text-gray-100 w-[420px] rounded-md py-12 px-6">
|
||||
<div className="flex flex-row items-center justify-center">
|
||||
<Dialog.Title className="text-xl">
|
||||
<img src={logo} className="inline w-12 h-12" alt="logo" /> Log in to twitch-clone
|
||||
<img src="./assets/images/logo.png" className="inline w-12 h-12" alt="logo" /> Log in to twitch-clone
|
||||
</Dialog.Title>
|
||||
</div>
|
||||
<Tab.Group defaultIndex={defaultPage}>
|
||||
@ -44,9 +41,7 @@ const LoginModal: FC<LoginModelProps> = ({ defaultPage, isOpen, onClose }) => {
|
||||
</Tab.Group>
|
||||
</Dialog.Panel>
|
||||
</div>
|
||||
</Dialog>,
|
||||
document.body
|
||||
);
|
||||
</Dialog>)
|
||||
};
|
||||
|
||||
export default LoginModal;
|
@ -1,8 +1,6 @@
|
||||
import { UserIcon } from '@heroicons/react/24/outline';
|
||||
import { FC, useState } from 'react';
|
||||
|
||||
import logo from '../assets/images/logo.png';
|
||||
|
||||
import Button from './Button';
|
||||
import Input from './Input';
|
||||
import LoginModal from './LoginModal';
|
||||
@ -27,7 +25,7 @@ const NavBar: FC = () => {
|
||||
<div className="basis-1/4">
|
||||
<ul className="flex flex-row space-x-8 items-center">
|
||||
<li>
|
||||
<img src={logo} className="w-8 h-8" alt="logo" />
|
||||
<img src="./assets/images/logo.png" className="w-8 h-8" alt="logo" />
|
||||
</li>
|
||||
<li>
|
||||
<p className="text-lg">Browse</p>
|
@ -1,8 +1,10 @@
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { FC } from 'react';
|
||||
import { SelfServiceRegistrationFlow } from '@ory/client';
|
||||
import { FC, useEffect, useState } from 'react';
|
||||
import { SubmitHandler, useForm } from 'react-hook-form';
|
||||
import { z } from 'zod';
|
||||
|
||||
|
||||
import FormField from './FormField';
|
||||
import InlineLink from './InlineLink';
|
||||
import SubmitButton from './SubmitButton';
|
||||
@ -24,12 +26,15 @@ const SignupFormSchema = z
|
||||
type SignupFormValues = z.infer<typeof SignupFormSchema>;
|
||||
|
||||
const SignupForm: FC = () => {
|
||||
// const navigate = useNavigate();
|
||||
// const { flow: flowId, return_to: returnTo } = useParams<{ flow?: string; return_to?: string }>();
|
||||
// const [flow, setFlow] = useState<SelfServiceRegistrationFlow>();
|
||||
const { register, handleSubmit } = useForm<SignupFormValues>({
|
||||
resolver: zodResolver(SignupFormSchema),
|
||||
});
|
||||
|
||||
const onSubmit: SubmitHandler<SignupFormValues> = (data) => {
|
||||
console.log({ data });
|
||||
const onSubmit: SubmitHandler<SignupFormValues> = async (data) => {
|
||||
console.log({data})
|
||||
};
|
||||
|
||||
return (
|
||||
@ -63,7 +68,7 @@ const SignupForm: FC = () => {
|
||||
/>
|
||||
<p className="text-sm text-center">
|
||||
By clicking Sign Up, you are agreeing to twitch-clone's{' '}
|
||||
<InlineLink to="https://tosdr.org/en/service/200" external>
|
||||
<InlineLink to="https://tosdr.org/en/service/200">
|
||||
Terms of Service
|
||||
</InlineLink>
|
||||
.
|
2
client/config/index.ts
Normal file
2
client/config/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export const API_URL = 'http://localhost:5000';
|
||||
export const KRATOS_URL = 'http://127.0.0.1:4433';
|
@ -1,16 +0,0 @@
|
||||
import { defineConfig } from 'cypress';
|
||||
|
||||
export default defineConfig({
|
||||
component: {
|
||||
devServer: {
|
||||
framework: 'react',
|
||||
bundler: 'vite',
|
||||
},
|
||||
},
|
||||
|
||||
e2e: {
|
||||
setupNodeEvents(on, config) {
|
||||
// implement node event listeners here
|
||||
},
|
||||
},
|
||||
});
|
3
client/cypress.json
Normal file
3
client/cypress.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"baseUrl": "http://localhost:3000"
|
||||
}
|
64
client/cypress/integration/pages.spec.js
Normal file
64
client/cypress/integration/pages.spec.js
Normal file
@ -0,0 +1,64 @@
|
||||
const randomString = () => (Math.random() + 1).toString(36).substring(7)
|
||||
const randomPassword = () => randomString() + randomString()
|
||||
const randomEmail = () => randomString() + '@' + randomString() + '.com'
|
||||
|
||||
const login = (email, password) => {
|
||||
cy.visit('/api/.ory/ui/login')
|
||||
cy.get('[name="identifier"]').type(email)
|
||||
cy.get('[name="password"]').type(password)
|
||||
cy.get('[name="method"]').click()
|
||||
loggedIn(email)
|
||||
}
|
||||
|
||||
const loggedIn = (email) => {
|
||||
cy.location('pathname').should('eq', '/')
|
||||
cy.get('[data-testid="session-content"]').should('contain.text', email)
|
||||
cy.get('[data-testid="logout"]').should('have.attr', 'aria-disabled', 'false')
|
||||
}
|
||||
|
||||
context('Basic UI interactions', () => {
|
||||
const email = randomEmail()
|
||||
const password = randomPassword()
|
||||
|
||||
beforeEach(() => {
|
||||
cy.clearCookies({ domain: null })
|
||||
})
|
||||
|
||||
it('can load the start page', () => {
|
||||
cy.visit('/')
|
||||
cy.get('a[href="/api/.ory/self-service/login/browser"]').should('exist')
|
||||
cy.get('a[href="/api/.ory/self-service/registration/browser"]').should(
|
||||
'exist'
|
||||
)
|
||||
})
|
||||
|
||||
it('redirects to login when accessing settings without session', () => {
|
||||
cy.visit('/api/.ory/ui/settings')
|
||||
cy.location('pathname').should('contain', 'api/.ory/ui/login')
|
||||
cy.get('[name="method"]').should('exist')
|
||||
})
|
||||
|
||||
it('can submit registration', () => {
|
||||
cy.visit('/api/.ory/ui/registration')
|
||||
cy.get('[name="traits.email"]').type(email)
|
||||
cy.get('[name="password"]').type(password)
|
||||
cy.get('[name="method"]').click()
|
||||
loggedIn(email)
|
||||
})
|
||||
|
||||
it('can load the login page', () => {
|
||||
login(email, password)
|
||||
})
|
||||
|
||||
it('goes to registration and clicks on log in and redirect works', () => {
|
||||
cy.visit('/api/.ory/ui/registration')
|
||||
cy.get('[data-testid="cta-link"]').click()
|
||||
login(email, password)
|
||||
})
|
||||
|
||||
it('can log out', () => {
|
||||
login(email, password)
|
||||
cy.get('a[data-testid="logout"]').click()
|
||||
cy.get('[data-testid="logout"]').should('not.exist')
|
||||
})
|
||||
})
|
22
client/cypress/plugins/index.js
Normal file
22
client/cypress/plugins/index.js
Normal file
@ -0,0 +1,22 @@
|
||||
/// <reference types="cypress" />
|
||||
// ***********************************************************
|
||||
// This example plugins/index.js can be used to load plugins
|
||||
//
|
||||
// You can change the location of this file or turn off loading
|
||||
// the plugins file with the 'pluginsFile' configuration option.
|
||||
//
|
||||
// You can read more here:
|
||||
// https://on.cypress.io/plugins-guide
|
||||
// ***********************************************************
|
||||
|
||||
// This function is called when a project is opened or re-opened (e.g. due to
|
||||
// the project's config changing)
|
||||
|
||||
/**
|
||||
* @type {Cypress.PluginConfig}
|
||||
*/
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
module.exports = (on, config) => {
|
||||
// `on` is used to hook into various events Cypress emits
|
||||
// `config` is the resolved Cypress config
|
||||
}
|
25
client/cypress/support/commands.js
Normal file
25
client/cypress/support/commands.js
Normal file
@ -0,0 +1,25 @@
|
||||
// ***********************************************
|
||||
// This example commands.js shows you how to
|
||||
// create various custom commands and overwrite
|
||||
// existing commands.
|
||||
//
|
||||
// For more comprehensive examples of custom
|
||||
// commands please read more here:
|
||||
// https://on.cypress.io/custom-commands
|
||||
// ***********************************************
|
||||
//
|
||||
//
|
||||
// -- This is a parent command --
|
||||
// Cypress.Commands.add('login', (email, password) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This is a child command --
|
||||
// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This is a dual command --
|
||||
// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This will overwrite an existing command --
|
||||
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
|
19
client/cypress/support/index.js
Normal file
19
client/cypress/support/index.js
Normal file
@ -0,0 +1,19 @@
|
||||
// ***********************************************************
|
||||
// This example support/index.js is processed and
|
||||
// loaded automatically before your test files.
|
||||
//
|
||||
// This is a great place to put global configuration and
|
||||
// behavior that modifies Cypress.
|
||||
//
|
||||
// You can change the location of this file or turn off
|
||||
// automatically serving support files with the
|
||||
// 'supportFile' configuration option.
|
||||
//
|
||||
// You can read more here:
|
||||
// https://on.cypress.io/configuration
|
||||
// ***********************************************************
|
||||
// Import commands.js using ES2015 syntax:
|
||||
import './commands'
|
||||
|
||||
// Alternatively you can use CommonJS syntax:
|
||||
// require('./commands')
|
@ -1,13 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Vite + React + TS</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
5
client/next-env.d.ts
vendored
Normal file
5
client/next-env.d.ts
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/basic-features/typescript for more information.
|
4
client/next.config.js
Normal file
4
client/next.config.js
Normal file
@ -0,0 +1,4 @@
|
||||
/** @type {import('next').NextConfig} */
|
||||
module.exports = {
|
||||
reactStrictMode: true
|
||||
}
|
@ -1,53 +1,47 @@
|
||||
{
|
||||
"name": "client",
|
||||
"name": "kratos-nextjs-react-example",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"config": {
|
||||
"prettierTarget": "{cypress,pages,styles,public,}{/**/,}*.{tsx,ts,json,md,js,css}"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc && vite build --outDir ../dist",
|
||||
"preview": "vite preview",
|
||||
"lint": "eslint --fix --ext .js,.ts,.tsx ./src --ignore-path .gitignore",
|
||||
"prettier": "prettier --ignore-path .gitignore --write \"**/*.+(js|json|ts|tsx)\"",
|
||||
"cypress": "cypress"
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint",
|
||||
"format": "prettier --write ${npm_package_config_prettierTarget}",
|
||||
"format:check": "prettier --check ${npm_package_config_prettierTarget}",
|
||||
"test": "cypress run",
|
||||
"test:dev": "cypress open"
|
||||
},
|
||||
"dependencies": {
|
||||
"@headlessui/react": "^1.7.2",
|
||||
"@heroicons/react": "^2.0.11",
|
||||
"@headlessui/react": "^1.7.3",
|
||||
"@heroicons/react": "^2.0.12",
|
||||
"@hookform/resolvers": "^2.9.8",
|
||||
"axios": "^0.27.2",
|
||||
"@ory/client": "0.0.1-alpha.169",
|
||||
"@ory/integrations": "0.2.5",
|
||||
"axios": "^1.1.2",
|
||||
"clsx": "^1.2.1",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-hook-form": "^7.36.1",
|
||||
"react-router-dom": "^6.4.1",
|
||||
"tailwind-scrollbar": "^2.0.1",
|
||||
"next": "12.1.5",
|
||||
"react": "17.0.2",
|
||||
"react-dom": "17.0.2",
|
||||
"react-hook-form": "^7.37.0",
|
||||
"tailwind-scrollbar": "2.1.0-preview.0",
|
||||
"zod": "^3.19.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@testing-library/cypress": "^8.0.3",
|
||||
"@types/react": "^18.0.17",
|
||||
"@types/react-dom": "^18.0.6",
|
||||
"@typescript-eslint/eslint-plugin": "^5.39.0",
|
||||
"@typescript-eslint/parser": "^5.39.0",
|
||||
"@vitejs/plugin-react": "^2.1.0",
|
||||
"@trivago/prettier-plugin-sort-imports": "^2.0.2",
|
||||
"@types/node": "16.11.6",
|
||||
"@types/react": "17.0.33",
|
||||
"autoprefixer": "^10.4.12",
|
||||
"cypress": "^10.9.0",
|
||||
"eslint": "^8.24.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-import-resolver-typescript": "^3.5.1",
|
||||
"eslint-plugin-cypress": "^2.12.1",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-jest-dom": "^4.0.2",
|
||||
"eslint-plugin-jsx-a11y": "^6.6.1",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"eslint-plugin-react": "^7.31.8",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"eslint-plugin-testing-library": "^5.7.2",
|
||||
"postcss": "^8.4.16",
|
||||
"prettier": "^2.7.1",
|
||||
"cypress": "^9.6.0",
|
||||
"eslint": "7.32.0",
|
||||
"eslint-config-next": "12.0.1",
|
||||
"ory-prettier-styles": "^1.1.2",
|
||||
"postcss": "^8.4.18",
|
||||
"prettier": "^2.3.2",
|
||||
"tailwindcss": "^3.1.8",
|
||||
"typescript": "^4.6.4",
|
||||
"vite": "^3.1.0"
|
||||
"typescript": "4.4.4"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,15 +1,18 @@
|
||||
import { ArrowRightIcon, HeartIcon, UserIcon } from '@heroicons/react/24/outline';
|
||||
|
||||
import Button from '../components/Button';
|
||||
import ChatMessage from '../components/ChatMessage';
|
||||
import Input from '../components/Input';
|
||||
import { numFormatter } from '../utils/format';
|
||||
import streams from '../placeholder/GetStreams';
|
||||
import Button from '../../components/Button';
|
||||
import ChatMessage from '../../components/ChatMessage';
|
||||
import Input from '../../components/Input';
|
||||
import { numFormatter } from '../../utils/format';
|
||||
import streams from '../../placeholder/GetStreams';
|
||||
import { NextPage } from 'next';
|
||||
import BrowseLayout from '../../components/BrowseLayout';
|
||||
|
||||
function ChannelPage() {
|
||||
const ChannelPage: NextPage = () => {
|
||||
const stream = streams.data[1];
|
||||
|
||||
return (
|
||||
<BrowseLayout>
|
||||
<div className="flex-1 flex flex-row">
|
||||
<div className="bg-neutral-900 flex-1">
|
||||
<div className="w-full h-auto aspect-video bg-red-200 " />
|
||||
@ -58,6 +61,7 @@ function ChannelPage() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</BrowseLayout>
|
||||
);
|
||||
}
|
||||
|
8
client/pages/_app.tsx
Normal file
8
client/pages/_app.tsx
Normal file
@ -0,0 +1,8 @@
|
||||
import '../styles/globals.css'
|
||||
import type { AppProps } from 'next/app'
|
||||
|
||||
function MyApp({ Component, pageProps }: AppProps) {
|
||||
return <Component {...pageProps} />
|
||||
}
|
||||
|
||||
export default MyApp
|
@ -1,4 +1,4 @@
|
||||
import { categories } from '../placeholder/SearchCategories';
|
||||
import { categories } from '../../placeholder/SearchCategories';
|
||||
|
||||
function ChannelPage() {
|
||||
const category = categories.data[0];
|
@ -1,8 +1,8 @@
|
||||
import { FC } from 'react';
|
||||
import { NextPage } from 'next';
|
||||
|
||||
import LoginModal from '../components/LoginModal';
|
||||
|
||||
const LoginPage: FC = () => {
|
||||
const LoginPage: NextPage = () => {
|
||||
return (
|
||||
<div className="bg-neutral-900 w-screen h-screen">
|
||||
<LoginModal isOpen={true} defaultPage={0} onClose={() => {}} />
|
@ -1,8 +1,8 @@
|
||||
import { FC } from 'react';
|
||||
|
||||
import { NextPage } from 'next';
|
||||
import LoginModal from '../components/LoginModal';
|
||||
|
||||
const SignupPage: FC = () => {
|
||||
const SignupPage: NextPage = () => {
|
||||
return (
|
||||
<div className="bg-neutral-900 w-screen h-screen">
|
||||
<LoginModal isOpen={true} defaultPage={1} onClose={() => {}} />
|
1921
client/pnpm-lock.yaml
generated
1921
client/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
BIN
client/public/favicon.ico
Normal file
BIN
client/public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 25 KiB |
4
client/public/vercel.svg
Normal file
4
client/public/vercel.svg
Normal file
@ -0,0 +1,4 @@
|
||||
<svg width="283" height="64" viewBox="0 0 283 64" fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M141.04 16c-11.04 0-19 7.2-19 18s8.96 18 20 18c6.67 0 12.55-2.64 16.19-7.09l-7.65-4.42c-2.02 2.21-5.09 3.5-8.54 3.5-4.79 0-8.86-2.5-10.37-6.5h28.02c.22-1.12.35-2.28.35-3.5 0-10.79-7.96-17.99-19-17.99zm-9.46 14.5c1.25-3.99 4.67-6.5 9.45-6.5 4.79 0 8.21 2.51 9.45 6.5h-18.9zM248.72 16c-11.04 0-19 7.2-19 18s8.96 18 20 18c6.67 0 12.55-2.64 16.19-7.09l-7.65-4.42c-2.02 2.21-5.09 3.5-8.54 3.5-4.79 0-8.86-2.5-10.37-6.5h28.02c.22-1.12.35-2.28.35-3.5 0-10.79-7.96-17.99-19-17.99zm-9.45 14.5c1.25-3.99 4.67-6.5 9.45-6.5 4.79 0 8.21 2.51 9.45 6.5h-18.9zM200.24 34c0 6 3.92 10 10 10 4.12 0 7.21-1.87 8.8-4.92l7.68 4.43c-3.18 5.3-9.14 8.49-16.48 8.49-11.05 0-19-7.2-19-18s7.96-18 19-18c7.34 0 13.29 3.19 16.48 8.49l-7.68 4.43c-1.59-3.05-4.68-4.92-8.8-4.92-6.07 0-10 4-10 10zm82.48-29v46h-9V5h9zM36.95 0L73.9 64H0L36.95 0zm92.38 5l-27.71 48L73.91 5H84.3l17.32 30 17.32-30h10.39zm58.91 12v9.69c-1-.29-2.06-.49-3.2-.49-5.81 0-10 4-10 10V51h-9V17h9v9.2c0-5.08 5.91-9.2 13.2-9.2z" fill="#000"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.1 KiB |
@ -1,14 +0,0 @@
|
||||
import { BrowserRouter } from 'react-router-dom';
|
||||
|
||||
import Routes from './routes';
|
||||
import './styles/global.css';
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<BrowserRouter>
|
||||
<Routes />
|
||||
</BrowserRouter>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
@ -1,26 +0,0 @@
|
||||
import clsx from 'clsx';
|
||||
import { FC } from 'react';
|
||||
import { NavLink } from 'react-router-dom';
|
||||
|
||||
export interface InlineLinkProps extends React.ComponentPropsWithoutRef<'span'> {
|
||||
to: string;
|
||||
external?: boolean;
|
||||
}
|
||||
|
||||
const InlineLink: FC<InlineLinkProps> = ({ to, external, className, ...rest }) => {
|
||||
if (external === true) {
|
||||
return (
|
||||
<a href={to}>
|
||||
<span className={clsx('text-violet-400 cursor-pointer text-sm', className)} {...rest} />
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<NavLink to={to}>
|
||||
<span className={clsx('text-violet-400 cursor-pointer text-sm', className)} {...rest} />
|
||||
</NavLink>
|
||||
);
|
||||
};
|
||||
|
||||
export default InlineLink;
|
@ -1 +0,0 @@
|
||||
export const API_URL = 'http://localhost:5000';
|
@ -1,7 +0,0 @@
|
||||
import { API_URL } from '@/config';
|
||||
import Axios from 'axios';
|
||||
|
||||
export const axios = Axios.create({
|
||||
baseURL: API_URL,
|
||||
withCredentials: true,
|
||||
});
|
@ -1,10 +0,0 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
|
||||
import App from './App';
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>
|
||||
);
|
@ -1,24 +0,0 @@
|
||||
import { FC } from 'react';
|
||||
import { Routes, Route } from 'react-router-dom';
|
||||
|
||||
import BrowseLayout from '../components/BrowseLayout';
|
||||
import CategoryPage from '../pages/CategoryPage';
|
||||
import ChannelPage from '../pages/ChannelPage';
|
||||
import LoginPage from '../pages/LoginPage';
|
||||
import SignupPage from '../pages/SignupPage';
|
||||
|
||||
const Router: FC = () => {
|
||||
return (
|
||||
<Routes>
|
||||
<Route path="/login" element={<LoginPage />} />
|
||||
<Route path="/signup" element={<SignupPage />} />
|
||||
<Route element={<BrowseLayout />}>
|
||||
<Route path="/:channel" element={<ChannelPage />} />
|
||||
<Route path="/category/:category" element={<CategoryPage />} />
|
||||
<Route path="/" element={<h1>Hi</h1>} />
|
||||
</Route>
|
||||
</Routes>
|
||||
);
|
||||
};
|
||||
|
||||
export default Router;
|
1
client/src/vite-env.d.ts
vendored
1
client/src/vite-env.d.ts
vendored
@ -1 +0,0 @@
|
||||
/// <reference types="vite/client" />
|
@ -1,6 +1,9 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: ["./index.html", "./src/**/*.tsx"],
|
||||
content: [
|
||||
"./pages/**/*.{js,ts,jsx,tsx}",
|
||||
"./components/**/*.{js,ts,jsx,tsx}",
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
fontFamily: {
|
||||
@ -9,4 +12,4 @@ module.exports = {
|
||||
},
|
||||
},
|
||||
plugins: [require("tailwind-scrollbar")({ nocompatible: true })],
|
||||
};
|
||||
}
|
@ -1,26 +1,20 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"useDefineForClassFields": true,
|
||||
"lib": ["DOM", "DOM.Iterable", "ESNext"],
|
||||
"allowJs": false,
|
||||
"target": "es5",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": false,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Node",
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx",
|
||||
"types": ["@testing-library/crypress", "cypress"],
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
"jsx": "preserve",
|
||||
"incremental": true
|
||||
},
|
||||
"include": ["src", "cypress"],
|
||||
"references": [{ "path": "./tsconfig.node.json" }]
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
@ -1,9 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Node",
|
||||
"allowSyntheticDefaultImports": true
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
import { defineConfig } from 'vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
});
|
Reference in New Issue
Block a user