diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 00000000..39cb3268 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,5 @@ +node_modules +coverage +html +dist +!.prettierrc.cjs \ No newline at end of file diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 4f6f59ee..4727f0ce 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -1,83 +1,68 @@ +// @ts-check + /** - * This is intended to be a basic starting point for linting in your app. - * It relies on recommended configs out of the box for simplicity, but you can - * and should modify this configuration to best suit your team's needs. + * To use this configuration, you need to install the following dependencies: + * + * eslint@^8.57.0 @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint-plugin-react eslint-plugin-react-hooks eslint-plugin-react-refresh eslint-plugin-unicorn eslint-plugin-unused-imports@^3.2.0 prettier eslint-config-prettier eslint-plugin-prettier @ianvs/prettier-plugin-sort-imports prettier-plugin-tailwindcss + * + * + * To Replicate the vitest configuration, you need to install the following dependencies: + * + * eslint-plugin-vitest@^0.4.1 eslint-testing-library + * + * Also replicate the .prettierrc.cjs file in the root of your project. */ /** @type {import('eslint').Linter.Config} */ module.exports = { root: true, - parserOptions: { - ecmaVersion: "latest", - sourceType: "module", - ecmaFeatures: { - jsx: true, - }, - }, + reportUnusedDisableDirectives: true, env: { browser: true, - commonjs: true, - es6: true, + es2020: true, }, - ignorePatterns: ["!**/.server", "!**/.client"], - - // Base config - extends: ["eslint:recommended"], - - overrides: [ - // React - { - files: ["**/*.{js,jsx,ts,tsx}"], - plugins: ["react", "jsx-a11y"], - extends: [ - "plugin:react/recommended", - "plugin:react/jsx-runtime", - "plugin:react-hooks/recommended", - "plugin:jsx-a11y/recommended", - ], - settings: { - react: { - version: "detect", - }, - formComponents: ["Form"], - linkComponents: [ - { name: "Link", linkAttribute: "to" }, - { name: "NavLink", linkAttribute: "to" }, - ], - "import/resolver": { - typescript: {}, - }, + extends: [ + "eslint:recommended", + "plugin:react/recommended", + "plugin:react/jsx-runtime", + "plugin:@typescript-eslint/recommended", + "plugin:react-hooks/recommended", + "plugin:unicorn/recommended", + "plugin:prettier/recommended", + ], + ignorePatterns: ["dist", "node_modules"], + parser: "@typescript-eslint/parser", + plugins: ["@typescript-eslint", "react", "unicorn", "unused-imports"], + rules: { + "react/prop-types": "off", + "unused-imports/no-unused-imports": "error", + "unused-imports/no-unused-vars": [ + "warn", + { + vars: "all", + varsIgnorePattern: "^_", + args: "after-used", + argsIgnorePattern: "^_", }, - }, - - // Typescript + ], + "unicorn/filename-case": [ + "error", + { cases: { kebabCase: true, pascalCase: true, camelCase: true } }, + ], + }, + settings: { + react: { version: "detect" }, + vitest: { typecheck: true }, + }, + overrides: [ { - files: ["**/*.{ts,tsx}"], - plugins: ["@typescript-eslint", "import"], - parser: "@typescript-eslint/parser", - settings: { - "import/internal-regex": "^~/", - "import/resolver": { - node: { - extensions: [".ts", ".tsx"], - }, - typescript: { - alwaysTryTypes: true, - }, - }, - }, - extends: [ - "plugin:@typescript-eslint/recommended", - "plugin:import/recommended", - "plugin:import/typescript", - ], + files: [".eslintrc.*js", ".vite(|st).(js|ts)"], + env: { node: true }, }, - - // Node { - files: [".eslintrc.cjs"], - env: { - node: true, + files: ["*.d.ts"], + rules: { + "unicorn/prevent-abbreviations": "off", }, }, ], diff --git a/.github/ISSUE_TEMPLATE/feature.yml b/.github/ISSUE_TEMPLATE/feature.yml new file mode 100644 index 00000000..d7afe15e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature.yml @@ -0,0 +1,38 @@ +name: Feature +description: An enhancement or feature +title: "[Feature]: " +labels: ["triage", "feature"] +body: + - type: textarea + id: description + attributes: + label: Description + description: A step-by-step description of the suggested feature/enhancement. + placeholder: On the user page, you should be able to... + validations: + required: true + - type: textarea + id: acceptanceCriteria + attributes: + label: Acceptance Criteria + description: What are the things that must be achieved for your ticket to be considered complete. + validations: + required: true + - type: markdown + attributes: + value: > + | Please include any screenshots which would help demonstrate the steps + and point out which parts the feature is related to + - type: textarea + id: links + attributes: + label: Links + description: Place links to supporting docs here. e.g. Figma + value: > + | [`FIGMA LINK`](LINK_HERE) + - type: textarea + id: images + attributes: + label: Images + description: Paste images or image urls + value: "![image](URL_HERE)" diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 00000000..905cdc0f --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,47 @@ + + +# Description + + + + + +**Closes #issue_number_here** + +# Changes proposed + +## What were you told to do? + + + +## What did you do? + + + +# Check List (Check all the applicable boxes) + +🚨Please review the [contribution guideline](CONTRIBUTING.md) for this repository. + + + + + +- [ ] My code follows the code style of this project. +- [ ] This PR does not contain plagiarized content. +- [ ] The title and description of the PR is clear and explains the approach. +- [ ] I am making a pull request against the **dev branch** (left side). +- [ ] My commit messages styles matches our requested structure. +- [ ] My code additions will fail neither code linting checks nor unit test. +- [ ] I am only making changes to files I was requested to. + +# Screenshots/Videos + + + + diff --git a/.github/workflows/dev-cicd.yml b/.github/workflows/dev-cicd.yml index aa75d6d2..d018fa33 100644 --- a/.github/workflows/dev-cicd.yml +++ b/.github/workflows/dev-cicd.yml @@ -24,25 +24,30 @@ jobs: with: node-version: '20' - - name: Cache npm modules + - name: Cache pnpm modules uses: actions/cache@v3 with: - path: ~/.npm - key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }} + path: ~/.pnpm-store + key: ${{ runner.os }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }} restore-keys: | - ${{ runner.os }}-npm- + ${{ runner.os }}-pnpm- - - name: Install npm - run: npm install -g npm@9 + - name: Install pnpm + uses: pnpm/action-setup@v4 + with: + version: 9 - name: Install dependencies - run: npm install + run: pnpm install - name: Lint code - run: npm run lint + run: pnpm lint + + - name: Build email + run: pnpm email:build - name: Build project - run: npm run build + run: pnpm build deploy: runs-on: ubuntu-latest diff --git a/.github/workflows/initial-label.yml b/.github/workflows/initial-label.yml new file mode 100644 index 00000000..a091548e --- /dev/null +++ b/.github/workflows/initial-label.yml @@ -0,0 +1,18 @@ +name: Label new issues +on: + issues: + types: + - reopened + - opened +jobs: + label_issues: + runs-on: ubuntu-latest + permissions: + issues: write + steps: + - run: gh issue edit "$NUMBER" --add-label "$LABELS" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_REPO: ${{ github.repository }} + NUMBER: ${{ github.event.issue.number }} + LABELS: triage diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 00000000..da2e4ccb --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,18 @@ +name: Lint + +on: + pull_request: + +jobs: + eslint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 18 + - uses: pnpm/action-setup@v4 + with: + version: 9 + - run: pnpm install + - run: pnpm lint diff --git a/.github/workflows/prod-cicd.yml b/.github/workflows/prod-cicd.yml index 64dd3d30..e64128c6 100644 --- a/.github/workflows/prod-cicd.yml +++ b/.github/workflows/prod-cicd.yml @@ -1,4 +1,3 @@ - name: Production CI/CD Pipeline on: @@ -25,25 +24,30 @@ jobs: with: node-version: '20' - - name: Cache npm modules + - name: Cache pnpm modules uses: actions/cache@v3 with: - path: ~/.npm - key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }} + path: ~/.pnpm-store + key: ${{ runner.os }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }} restore-keys: | - ${{ runner.os }}-npm- + ${{ runner.os }}-pnpm- - - name: Install npm - run: npm install -g npm@9 + - name: Install pnpm + uses: pnpm/action-setup@v4 + with: + version: 9 - name: Install dependencies - run: npm install + run: pnpm install - name: Lint code - run: npm run lint + run: pnpm run lint + + - name: Build email + run: pnpm email:build - name: Build project - run: npm run build + run: pnpm run build deploy: runs-on: ubuntu-latest diff --git a/.github/workflows/staging-cicd.yml b/.github/workflows/staging-cicd.yml new file mode 100644 index 00000000..f5eabfd2 --- /dev/null +++ b/.github/workflows/staging-cicd.yml @@ -0,0 +1,64 @@ +name: Staging CI/CD Pipeline + +on: + push: + branches: + - staging + paths-ignore: + - .github/workflows/** + +concurrency: + group: ${{ github.ref }} + cancel-in-progress: true + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Set up Node.js + uses: actions/setup-node@v3 + with: + node-version: '20' + + - name: Cache pnpm modules + uses: actions/cache@v3 + with: + path: ~/.pnpm-store + key: ${{ runner.os }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm- + + - name: Install pnpm + uses: pnpm/action-setup@v4 + with: + version: 9 + + - name: Install dependencies + run: pnpm install + + - name: Lint code + run: pnpm run lint + + - name: Build email + run: pnpm email:build + + - name: Build project + run: pnpm run build + + deploy: + runs-on: ubuntu-latest + needs: build + + steps: + - name: Deploy to staging environment + uses: appleboy/ssh-action@v1.0.3 + with: + host: ${{ secrets.HOST }} + username: ${{ secrets.USERNAME }} + password: ${{ secrets.PASSWORD }} + script: | + ./deploy_staging.sh remix diff --git a/.gitignore b/.gitignore index 80ec311f..b689d8e2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ node_modules - +/.react-email /.cache /build .env +*.DS_Store diff --git a/.prettierrc.cjs b/.prettierrc.cjs new file mode 100644 index 00000000..a9a84453 --- /dev/null +++ b/.prettierrc.cjs @@ -0,0 +1,22 @@ +/** @type {import('prettier').Config & import("@ianvs/prettier-plugin-sort-imports").PluginConfig} */ +module.exports = { + tabWidth: 2, + printWidth: 80, + jsxSingleQuote: false, + singleQuote: false, + semi: true, + trailingComma: "all", + arrowParens: "always", + endOfLine: "auto", + + plugins: [ + "@ianvs/prettier-plugin-sort-imports", + "prettier-plugin-tailwindcss", + ], + + // #region @ianvs/prettier-plugin-sort-imports + importOrder: ["", "", "^~/", "^[.][.]/", "^[.]/"], + importOrderParserPlugins: ["typescript", "jsx", "decorators-legacy"], + importOrderTypeScriptVersion: "5.0.0", + // #endregion @ianvs/prettier-plugin-sort-imports +}; diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 00000000..897af65d --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,5 @@ +{ + "recommendations": [ + "dbaeumer.vscode-eslint" + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..7d8a366c --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,12 @@ +{ + "[javascript][javascriptreact][typescript][typescriptreact]": { + "editor.formatOnSave": true, + "editor.defaultFormatter": "vscode.typescript-language-features" + }, + "editor.codeActionsOnSave": { + "source.fixAll.eslint": "always" + }, + "[typescriptreact]": { + "editor.defaultFormatter": "vscode.typescript-language-features" + }, +} \ No newline at end of file diff --git a/ROUTING.md b/ROUTING.md new file mode 100644 index 00000000..e95360e5 --- /dev/null +++ b/ROUTING.md @@ -0,0 +1,52 @@ +- / +- pricing +- about +- help +- legal + - privacy-policy + - terms-and-condition +- career + - :id (individual jobs page) +- trial (squeeze page) +- blog + - latest (latest articles page) + - :id (Individual blog page) +- contact +- faq +- waitlist +- auth + - register + - ?otp + - organisation + - login + - email-link + - success + - forgot-password + - verify-otp + - success + - reset-password +- dashboard + - products + - add + - :id + - settings + - profile + - account-security + - password + - payment-information + - checkout + - cancel-subscription + - notification + - payment-information + - data-and-privacy + - language-and-region +- admin + - dashboard + - products + - add + - :id + - users + - email-templates + - squeeze + - waitlist + - settings diff --git a/app/components/.DS_Store b/app/components/.DS_Store new file mode 100644 index 00000000..5af7a684 Binary files /dev/null and b/app/components/.DS_Store differ diff --git a/app/components/BlogCards.tsx b/app/components/BlogCards.tsx new file mode 100644 index 00000000..3c3b080b --- /dev/null +++ b/app/components/BlogCards.tsx @@ -0,0 +1,58 @@ +interface BlogCardProperties { + title: string; + description: string; + date: string; + authorName: string; + authorProfilePicture: string; + tag: string; + timeOfReading: string; + blogImage: string; + link: string; +} + +const BlogCard: React.FC = ({ + title, + description, + date, + authorName, + authorProfilePicture, + tag, + timeOfReading, + blogImage, + link, +}) => { + return ( +
+ {title} +
+
+ + + {tag} +
+
+ +

{title}

+
+

{description}

+
+
+ {date} + {timeOfReading} mins Read +
+
+ {authorName} +
+

{authorName}

+
+
+
+
+ ); +}; + +export default BlogCard; diff --git a/app/components/BlogPost.tsx b/app/components/BlogPost.tsx new file mode 100644 index 00000000..e6f90eb4 --- /dev/null +++ b/app/components/BlogPost.tsx @@ -0,0 +1,75 @@ +import RecentBlogCard from "./RecentBlogCard"; +import { Button } from "./ui/button"; + +const blogPosts = [ + { + title: "The Power of Networking: How to Build Meaningful connections", + date: "Jul 12, 2024", + timeRead: "5 mins read", + image: "/images/business.jpg", + description: "Business", + variant: 0, + }, + { + title: "The Global Impact of Climate Change: A Look at the Evidence", + date: "Jul 12, 2024", + timeRead: "5 mins read", + image: "/images/nature.png", + description: "World News", + variant: 1, + }, + { + title: "5 Easy and Delicious Recipes for Busy Weeknights", + date: "Jul 12, 2024", + timeRead: "5 mins read", + image: "/images/food1.jpg", + description: "Food", + variant: 2, + }, + { + title: "5 Simple Habits to Improve Your Mental Wellbeing", + date: "Jul 12, 2024", + timeRead: "5 mins read", + image: "/images/yoga.jpg", + description: "Lifestyle", + variant: 3, + }, + { + title: "The Ultimate Guide to Dressing Stylishly with Fewer Clothes", + date: "Jul 12, 2024", + timeRead: "5 mins read", + image: "/images/fashion.jpg", + description: "Fashion", + variant: 4, + }, + { + title: "The Future of Travel: What Will the World Look Like in 2030?", + date: "Jul 12, 2024", + timeRead: "5 mins read", + image: "/images/person with glasses.jpg", + description: "World News", + variant: 5, + }, +]; + +const BlogPost = () => { + return ( +
+

+ Recent Blog posts +

+
+ {blogPosts.map((post, index) => ( + + ))} +
+
+ +
+
+ ); +}; + +export default BlogPost; diff --git a/app/components/BreadCrumbs.tsx b/app/components/BreadCrumbs.tsx new file mode 100644 index 00000000..65882a15 --- /dev/null +++ b/app/components/BreadCrumbs.tsx @@ -0,0 +1,125 @@ +import { Slot } from "@radix-ui/react-slot"; +import { ChevronRight, MoreHorizontal } from "lucide-react"; +import { + forwardRef, + type ComponentProps, + type ComponentPropsWithoutRef, + type ReactNode, +} from "react"; + +import { cn } from "~/lib/utils/cn"; + +const Breadcrumb = forwardRef< + HTMLElement, + ComponentPropsWithoutRef<"nav"> & { + separator?: ReactNode; + } +>(({ ...properties }, reference) => ( +