Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -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
124 changes: 124 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -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
4 changes: 4 additions & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

npm run pre-commit
10 changes: 10 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.next
node_modules
.git
.env*
*.log
.vercel
.convex/_generated
dist
build
coverage
13 changes: 13 additions & 0 deletions .prettierrc.json
Original file line number Diff line number Diff line change
@@ -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"]
}
12 changes: 6 additions & 6 deletions app/(dashboard)/_components/board-card/footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,25 +19,25 @@ export const Footer = ({
disabled,
}: FooterProps) => {
const handleClick = (
event: React.MouseEvent<HTMLButtonElement, MouseEvent>,
event: React.MouseEvent<HTMLButtonElement, MouseEvent>
) => {
event.stopPropagation();
event.preventDefault();
onClick();
};
return (
<div className="relative bg-white p-2 group">
<p className="text-[13px] truncate max-w-[calc(100%-20px)]">{title}</p>
<p className="opacity-0 group-hover:opacity-100 transition-opacity text-[11px] text-muted-foreground truncate">
<div className="group relative bg-white p-2">
<p className="max-w-[calc(100%-20px)] truncate text-[13px]">{title}</p>
<p className="truncate text-[11px] text-muted-foreground opacity-0 transition-opacity group-hover:opacity-100">
{authorLabel}, {createdAtLabel}
</p>
<button
disabled={disabled}
onClick={handleClick}
aria-label="Favorite"
className={cn(
"opacity-0 group-hover:opacity-100 transition-opacity absolute top-3 right-3 text-muted-foreground hover:text-blue-600",
disabled && "cursor-not-allowed opacity-75",
"absolute right-3 top-3 text-muted-foreground opacity-0 transition-opacity hover:text-blue-600 group-hover:opacity-100",
disabled && "cursor-not-allowed opacity-75"
)}
>
<Star
Expand Down
12 changes: 6 additions & 6 deletions app/(dashboard)/_components/board-card/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@ export const BoardCard = ({
});

const { mutate: onFavorite, pending: pendingFavorite } = useApiMutation(
api.board.favorite,
api.board.favorite
);
const { mutate: onUnfavorite, pending: pendingUnfavorite } = useApiMutation(
api.board.unfavorite,
api.board.unfavorite
);

const toggleFavorite = () => {
Expand All @@ -59,13 +59,13 @@ export const BoardCard = ({
};
return (
<Link href={`/board/${id}`}>
<div className="group aspect-[100/127] border rounded-lg flex flex-col justify-between overflow-hidden">
<div className="group flex aspect-[100/127] flex-col justify-between overflow-hidden rounded-lg border">
<div className="relative flex-1 bg-amber-50">
<Image src={imageUrl} alt={title} fill className="object-fit" />
<Overlay />
<Actions id={id} title={title} side="right">
<button className="absolute top-1 right-1 opacity-0 group-hover:opacity-100 transition-opacity px-3 py-2 outline-none">
<MoreHorizontal className="text-white opacity-75 hover:opacity-100 transition-opacity" />
<button className="absolute right-1 top-1 px-3 py-2 opacity-0 outline-none transition-opacity group-hover:opacity-100">
<MoreHorizontal className="text-white opacity-75 transition-opacity hover:opacity-100" />
</button>
</Actions>
</div>
Expand All @@ -85,7 +85,7 @@ export const BoardCard = ({

BoardCard.Skeleton = function BoardCardSkeleton() {
return (
<div className="aspect-[100/127] rounded-lg overflow-hidden">
<div className="aspect-[100/127] overflow-hidden rounded-lg">
<Skeleton className="h-full w-full" />
</div>
);
Expand Down
2 changes: 1 addition & 1 deletion app/(dashboard)/_components/board-card/overlay.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export const Overlay = () => {
return (
<div className="opacity-0 group-hover:opacity-50 transition-opacity h-full w-full bg-black" />
<div className="h-full w-full bg-black opacity-0 transition-opacity group-hover:opacity-50" />
);
};
8 changes: 4 additions & 4 deletions app/(dashboard)/_components/board-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -36,7 +36,7 @@ export const BoardList = ({ orgId }: BoardListProps) => {
<h2 className="text-3xl">
{favorites ? "Favorite Boards" : "Team Boards"}
</h2>
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-4 lg:grid-cols-4 xl:grid-cols-5 2xl:grid-cols-6 gap-5 mt-8 pb-10">
<div className="mt-8 grid grid-cols-1 gap-5 pb-10 sm:grid-cols-2 md:grid-cols-4 lg:grid-cols-4 xl:grid-cols-5 2xl:grid-cols-6">
<NewBoardButton orgId={orgId} disabled />
<BoardCard.Skeleton />
<BoardCard.Skeleton />
Expand Down Expand Up @@ -64,7 +64,7 @@ export const BoardList = ({ orgId }: BoardListProps) => {
<h2 className="text-3xl">
{favorites ? "Favorite Boards" : "Team Boards"}
</h2>
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-4 lg:grid-cols-4 xl:grid-cols-5 2xl:grid-cols-6 gap-5 mt-8 pb-10">
<div className="mt-8 grid grid-cols-1 gap-5 pb-10 sm:grid-cols-2 md:grid-cols-4 lg:grid-cols-4 xl:grid-cols-5 2xl:grid-cols-6">
<NewBoardButton orgId={orgId} />
{data.map((board) => (
<BoardCard
Expand Down
6 changes: 3 additions & 3 deletions app/(dashboard)/_components/empty-boards.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ export const EmptyBoards = () => {
};

return (
<div className="h-full flex flex-col items-center justify-center">
<div className="flex h-full flex-col items-center justify-center">
<Image src="/note.svg" height={110} width={110} alt="Empty" />
<h2 className="text-2xl font-semibold mt-6">Create your first board!</h2>
<p className="text-muted-foreground text-sm mt-2">
<h2 className="mt-6 text-2xl font-semibold">Create your first board!</h2>
<p className="mt-2 text-sm text-muted-foreground">
Start by creating a board for your organization.
</p>
<div className="mt-6">
Expand Down
6 changes: 3 additions & 3 deletions app/(dashboard)/_components/empty-favorites.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import Image from "next/image";

export const EmptyFavorites = () => {
return (
<div className="h-full flex flex-col items-center justify-center">
<div className="flex h-full flex-col items-center justify-center">
<Image src="/empty-favourites.svg" height={140} width={140} alt="Empty" />
<h2 className="text-2xl font-semibold mt-6">No favorite Boards !</h2>
<p className="text-muted-foreground textg-sm mt-2">
<h2 className="mt-6 text-2xl font-semibold">No favorite Boards !</h2>
<p className="textg-sm mt-2 text-muted-foreground">
Try favoriting a board .
</p>
</div>
Expand Down
8 changes: 4 additions & 4 deletions app/(dashboard)/_components/empty-org.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ import { Dialog, DialogContent, DialogTrigger } from "@/components/ui/dialog";

export const EmptyOrg = () => {
return (
<div className="h-full p-20 mt-100 flex flex-col items-center justify-center">
<div className="mt-100 flex h-full flex-col items-center justify-center p-20">
<Image src="/elements.svg" alt="Empty" height={200} width={200} />
<h2 className="text-2xl font-semibold mt-6">Welcome to Flowboard</h2>
<p className="text-muted-foreground text-sm mt-2">
<h2 className="mt-6 text-2xl font-semibold">Welcome to Flowboard</h2>
<p className="mt-2 text-sm text-muted-foreground">
Create an organization to get started
</p>
<div className="mt-6">
Expand All @@ -19,7 +19,7 @@ export const EmptyOrg = () => {
<DialogTrigger asChild>
<Button size="lg">Create Organization</Button>
</DialogTrigger>
<DialogContent className="p-0 bg-transparent border-none max-w-[480px]">
<DialogContent className="max-w-[480px] border-none bg-transparent p-0">
<CreateOrganization />
</DialogContent>
</Dialog>
Expand Down
6 changes: 3 additions & 3 deletions app/(dashboard)/_components/empty-search.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import Image from "next/image";

export const EmptySearch = () => {
return (
<div className="h-full flex flex-col items-center justify-center">
<div className="flex h-full flex-col items-center justify-center">
<Image src="/empty-search.svg" height={140} width={140} alt="Empty" />
<h2 className="text-2xl font-semibold mt-6">No results Found !</h2>
<p className="text-muted-foreground textg-sm mt-2">
<h2 className="mt-6 text-2xl font-semibold">No results Found !</h2>
<p className="textg-sm mt-2 text-muted-foreground">
Try Searching for something else .
</p>
</div>
Expand Down
4 changes: 2 additions & 2 deletions app/(dashboard)/_components/invite-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ export const InviteButton = () => {
<Dialog>
<DialogTrigger asChild>
<Button variant="outline">
<Plus className="h-4 w-4 mr-2" />
<Plus className="mr-2 h-4 w-4" />
Invite Members
</Button>
</DialogTrigger>
<DialogContent className="p-0 bg-transparent border-none max-w-[880px]">
<DialogContent className="max-w-[880px] border-none bg-transparent p-0">
{/* Add DialogTitle wrapped in VisuallyHidden for accessibility */}
<VisuallyHidden>
<DialogTitle>Organization Management</DialogTitle>
Expand Down
2 changes: 1 addition & 1 deletion app/(dashboard)/_components/navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export const Navbar = () => {
</div>

{/* Organization Switcher for small screens */}
<div className="block lg:hidden flex-1">
<div className="block flex-1 lg:hidden">
<OrganizationSwitcher
hidePersonal
appearance={{
Expand Down
8 changes: 4 additions & 4 deletions app/(dashboard)/_components/new-board-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,14 @@ export const NewBoardButton = ({ orgId, disabled }: NewBoardButtonProps) => {
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"
)}
>
<div />
<Plus className="h-12 w-12 text-white stroke-1 " />
<p className="text-sm text-white font-light">New Board</p>
<Plus className="h-12 w-12 stroke-1 text-white " />
<p className="text-sm font-light text-white">New Board</p>
</button>
);
};
Loading
Loading