diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..a15c68c --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,23 @@ +## Description + +Brief description of changes + +## Type of Change + +- [ ] Bug fix +- [ ] New feature +- [ ] Breaking change +- [ ] Documentation update + +## Testing + +- [ ] Tests pass locally +- [ ] Code builds successfully +- [ ] Manual testing completed + +## Checklist + +- [ ] Code follows project style guidelines +- [ ] Self-review completed +- [ ] Code is properly commented +- [ ] No new warnings introduced diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..70eab09 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,124 @@ +name: CI + +on: + push: + branches: [ main, develop ] + pull_request: + branches: [ main ] + +jobs: + build-and-test: + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [18.x, 20.x] + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Run Prettier check + id: prettier-check + run: npm run format:check + continue-on-error: true + + - name: Auto-fix formatting if needed + if: steps.prettier-check.outcome == 'failure' + run: | + echo "Formatting issues found. Auto-fixing..." + npm run format + echo "Code has been auto-formatted" + + - name: Verify formatting after fix + if: steps.prettier-check.outcome == 'failure' + run: npm run format:check + + - name: Commit formatted code + if: steps.prettier-check.outcome == 'failure' && github.event_name == 'push' + run: | + git config --local user.email "action@github.com" + git config --local user.name "GitHub Action" + git add . + if git diff --staged --quiet; then + echo "No changes to commit" + else + git commit -m "Auto-format code with Prettier [skip ci]" + git push + fi + + - name: Run ESLint + run: npm run lint + + - name: Auto-fix ESLint issues + if: failure() + run: | + echo "ESLint issues found. Attempting auto-fix..." + npm run lint:fix + echo "ESLint auto-fix completed" + + - name: Run TypeScript check + run: npm run type-check + + - name: Build application + run: npm run build + env: + NEXT_PUBLIC_CONVEX_URL: ${{ secrets.NEXT_PUBLIC_CONVEX_URL }} + NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY: ${{ secrets.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY }} + CLERK_SECRET_KEY: ${{ secrets.CLERK_SECRET_KEY }} + LIVEBLOCKS_SECRET_KEY: ${{ secrets.LIVEBLOCKS_SECRET_KEY }} + + - name: Upload build artifacts + uses: actions/upload-artifact@v4 + if: success() + with: + name: build-files-${{ matrix.node-version }} + path: .next/ + retention-days: 1 + + format-and-lint-fix: + runs-on: ubuntu-latest + if: github.event_name == 'pull_request' + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + token: ${{ secrets.GITHUB_TOKEN }} + ref: ${{ github.head_ref }} + + - name: Use Node.js 20.x + uses: actions/setup-node@v4 + with: + node-version: 20.x + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Run Prettier format + run: npm run format + + - name: Run ESLint fix + run: npm run lint:fix + + - name: Commit changes + run: | + git config --local user.email "action@github.com" + git config --local user.name "GitHub Action" + git add . + if git diff --staged --quiet; then + echo "No formatting changes needed" + else + git commit -m "Auto-format and lint-fix code [skip ci]" + git push + fi \ No newline at end of file diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 0000000..14ccce4 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,4 @@ +#!/usr/bin/env sh +. "$(dirname -- "$0")/_/husky.sh" + +npm run pre-commit \ No newline at end of file diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..216f883 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,10 @@ +.next +node_modules +.git +.env* +*.log +.vercel +.convex/_generated +dist +build +coverage \ No newline at end of file diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..d597ae6 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,13 @@ +{ + "semi": true, + "trailingComma": "es5", + "singleQuote": false, + "printWidth": 80, + "tabWidth": 2, + "useTabs": false, + "bracketSpacing": true, + "bracketSameLine": false, + "arrowParens": "always", + "endOfLine": "lf", + "plugins": ["prettier-plugin-tailwindcss"] +} diff --git a/app/(dashboard)/_components/board-card/footer.tsx b/app/(dashboard)/_components/board-card/footer.tsx index 918ff22..1583973 100644 --- a/app/(dashboard)/_components/board-card/footer.tsx +++ b/app/(dashboard)/_components/board-card/footer.tsx @@ -19,16 +19,16 @@ export const Footer = ({ disabled, }: FooterProps) => { const handleClick = ( - event: React.MouseEvent, + event: React.MouseEvent ) => { event.stopPropagation(); event.preventDefault(); onClick(); }; return ( -
-

{title}

-

+

+

{title}

+

{authorLabel}, {createdAtLabel}

@@ -85,7 +85,7 @@ export const BoardCard = ({ BoardCard.Skeleton = function BoardCardSkeleton() { return ( -
+
); diff --git a/app/(dashboard)/_components/board-card/overlay.tsx b/app/(dashboard)/_components/board-card/overlay.tsx index e73b105..291467e 100644 --- a/app/(dashboard)/_components/board-card/overlay.tsx +++ b/app/(dashboard)/_components/board-card/overlay.tsx @@ -1,5 +1,5 @@ export const Overlay = () => { return ( -
+
); }; diff --git a/app/(dashboard)/_components/board-list.tsx b/app/(dashboard)/_components/board-list.tsx index 689ad0f..0e8cd11 100644 --- a/app/(dashboard)/_components/board-list.tsx +++ b/app/(dashboard)/_components/board-list.tsx @@ -20,11 +20,11 @@ export const BoardList = ({ orgId }: BoardListProps) => { // Use `useMemo()` to extract values safely const search = useMemo( () => searchParams.get("search") || undefined, - [searchParams], + [searchParams] ); const favorites = useMemo( () => searchParams.get("favorites") || undefined, - [searchParams], + [searchParams] ); // Fetch data using query params @@ -36,7 +36,7 @@ export const BoardList = ({ orgId }: BoardListProps) => {

{favorites ? "Favorite Boards" : "Team Boards"}

-
+
@@ -64,7 +64,7 @@ export const BoardList = ({ orgId }: BoardListProps) => {

{favorites ? "Favorite Boards" : "Team Boards"}

-
+
{data.map((board) => ( { }; return ( -
+
Empty -

Create your first board!

-

+

Create your first board!

+

Start by creating a board for your organization.

diff --git a/app/(dashboard)/_components/empty-favorites.tsx b/app/(dashboard)/_components/empty-favorites.tsx index 8ba2a60..4522765 100644 --- a/app/(dashboard)/_components/empty-favorites.tsx +++ b/app/(dashboard)/_components/empty-favorites.tsx @@ -2,10 +2,10 @@ import Image from "next/image"; export const EmptyFavorites = () => { return ( -
+
Empty -

No favorite Boards !

-

+

No favorite Boards !

+

Try favoriting a board .

diff --git a/app/(dashboard)/_components/empty-org.tsx b/app/(dashboard)/_components/empty-org.tsx index 6121b9f..d875162 100644 --- a/app/(dashboard)/_components/empty-org.tsx +++ b/app/(dashboard)/_components/empty-org.tsx @@ -7,10 +7,10 @@ import { Dialog, DialogContent, DialogTrigger } from "@/components/ui/dialog"; export const EmptyOrg = () => { return ( -
+
Empty -

Welcome to Flowboard

-

+

Welcome to Flowboard

+

Create an organization to get started

@@ -19,7 +19,7 @@ export const EmptyOrg = () => { - + diff --git a/app/(dashboard)/_components/empty-search.tsx b/app/(dashboard)/_components/empty-search.tsx index ac6011f..5c14baa 100644 --- a/app/(dashboard)/_components/empty-search.tsx +++ b/app/(dashboard)/_components/empty-search.tsx @@ -2,10 +2,10 @@ import Image from "next/image"; export const EmptySearch = () => { return ( -
+
Empty -

No results Found !

-

+

No results Found !

+

Try Searching for something else .

diff --git a/app/(dashboard)/_components/invite-button.tsx b/app/(dashboard)/_components/invite-button.tsx index f53f018..339fc02 100644 --- a/app/(dashboard)/_components/invite-button.tsx +++ b/app/(dashboard)/_components/invite-button.tsx @@ -16,11 +16,11 @@ export const InviteButton = () => { - + {/* Add DialogTitle wrapped in VisuallyHidden for accessibility */} Organization Management diff --git a/app/(dashboard)/_components/navbar.tsx b/app/(dashboard)/_components/navbar.tsx index 2833977..bb35eb7 100644 --- a/app/(dashboard)/_components/navbar.tsx +++ b/app/(dashboard)/_components/navbar.tsx @@ -27,7 +27,7 @@ export const Navbar = () => {
{/* Organization Switcher for small screens */} -
+
{ disabled={pending || disabled} onClick={onClick} className={cn( - "col-span-1 aspect-[100/127] bg-blue-600 rounded-lg hover:bg-blue-800 flex flex-col items-center justify-center py-6", + "col-span-1 flex aspect-[100/127] flex-col items-center justify-center rounded-lg bg-blue-600 py-6 hover:bg-blue-800", (pending || disabled) && - "opacity-75 hover:bg-blue-600 cursor-not-allowed", + "cursor-not-allowed opacity-75 hover:bg-blue-600" )} >
- -

New Board

+ +

New Board

); }; diff --git a/app/(dashboard)/_components/org-sidebar.tsx b/app/(dashboard)/_components/org-sidebar.tsx index f6fdb7d..3e0c66d 100644 --- a/app/(dashboard)/_components/org-sidebar.tsx +++ b/app/(dashboard)/_components/org-sidebar.tsx @@ -18,11 +18,11 @@ export const OrgSidebar = () => { const favorites = searchParams.get("favorites"); return ( -
+
logo - + Board
@@ -48,15 +48,15 @@ export const OrgSidebar = () => { }, }} /> -
+
@@ -64,7 +64,7 @@ export const OrgSidebar = () => { variant={favorites ? "secondary" : "outline"} asChild size="lg" - className="font-normal justify-start px-2 w-full" + className="w-full justify-start px-2 font-normal" > { }, }} > - + Favorite Boards diff --git a/app/(dashboard)/_components/search-input.tsx b/app/(dashboard)/_components/search-input.tsx index d6309b6..ad9e717 100644 --- a/app/(dashboard)/_components/search-input.tsx +++ b/app/(dashboard)/_components/search-input.tsx @@ -39,15 +39,15 @@ export const SearchInput = () => { search: debouncedValue, }, }, - { skipEmptyString: true, skipNull: true }, + { skipEmptyString: true, skipNull: true } ); router.push(url); }, [debouncedValue, router]); return ( -
- +
+ { return ( -