diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..d23bfb6 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,13 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_style = space +indent_size = 2 +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +# Markdown line breaks come from trailing spaces; don't strip them. +trim_trailing_whitespace = false diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..a3427b1 --- /dev/null +++ b/.github/CODE_OF_CONDUCT.md @@ -0,0 +1,28 @@ +# Code of Conduct + +This project adopts the **[Contributor Covenant, version 2.1][covenant]**. + +By participating in this project — filing an issue, opening a PR, commenting +on one, or interacting in any other community space — you agree to uphold +that standard. + +## Reporting + +If you witness or experience behaviour that violates the Code, please +report it privately to the project maintainer: + +- Email: **a.donauskas@hostinger.com** with `[WaCRM conduct]` in the + subject. + +Reports are handled confidentially. Expect an acknowledgement within +72 hours and a decision on next steps within a week. + +## Enforcement + +The maintainer is responsible for enforcement and will apply the community +impact guidelines described in the [Contributor Covenant][covenant-enforce] +— ranging from a private correction to a permanent ban, proportional to the +behaviour. + +[covenant]: https://www.contributor-covenant.org/version/2/1/code_of_conduct/ +[covenant-enforce]: https://www.contributor-covenant.org/version/2/1/code_of_conduct/#enforcement-guidelines diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..82ed93d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,76 @@ +name: Bug report +description: Something in WaCRM isn't working the way the docs say it should. +title: "[bug] " +labels: ["bug", "triage"] +body: + - type: markdown + attributes: + value: | + Thanks for reporting! The more specific you can be, the faster we + can land a fix. + + **Not a bug in the code?** If this is a security issue, close this + form and follow [SECURITY.md](https://github.com/ArnasDon/wacrm/blob/main/.github/SECURITY.md) + instead. + + - type: textarea + id: summary + attributes: + label: What happened? + description: One or two sentences describing the symptom. + placeholder: Clicking a conversation leaves the thread stuck on "No messages yet". + validations: + required: true + + - type: textarea + id: steps + attributes: + label: Steps to reproduce + description: The minimum steps we need to trigger the bug on our side. + placeholder: | + 1. Sign in, go to /inbox. + 2. Click any conversation that has past messages. + 3. Thread pane shows "No messages yet" until hard refresh. + validations: + required: true + + - type: textarea + id: expected + attributes: + label: What did you expect? + placeholder: Messages load the first time, every time. + validations: + required: true + + - type: input + id: version + attributes: + label: Commit / version + description: | + The commit SHA or release you're on. `git rev-parse --short HEAD` + in the fork works. + placeholder: "e.g. d6a4677 or v0.2.0" + validations: + required: false + + - type: dropdown + id: runtime + attributes: + label: Where is it running? + options: + - Local dev (npm run dev) + - Hostinger Managed Node.js + - Hostinger VPS + - Vercel + - Other Node host + validations: + required: false + + - type: textarea + id: logs + attributes: + label: Logs / screenshots + description: Server logs, browser console errors, network tab — anything that looks suspicious. Scrub tokens before pasting. + render: text + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..cc84b43 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,11 @@ +blank_issues_enabled: false +contact_links: + - name: Security vulnerability (private) + url: https://github.com/ArnasDon/wacrm/security/advisories/new + about: Do not file security issues in public. Follow the private disclosure flow. + - name: Setup / "how do I..." questions + url: https://github.com/ArnasDon/wacrm/blob/main/docs/README.md + about: Check the docs first — setup, deploy, troubleshooting are all covered. + - name: Using WaCRM as a template (forking) + url: https://github.com/ArnasDon/wacrm/blob/main/CONTRIBUTING.md + about: WaCRM is a template. Most changes belong in your fork, not an upstream issue — here's how that works. diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000..a2cd019 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,68 @@ +name: Feature request +description: Propose a new feature or a meaningful enhancement. +title: "[feat] " +labels: ["enhancement", "triage"] +body: + - type: markdown + attributes: + value: | + Thanks for the idea — but read this first. + + WaCRM is a **template**, not a collaborative product. The + upstream scope is intentionally narrow, so most feature + requests end up as *"build this in your fork"* rather than + landing here. That's the point of a template. + + When an upstream feature request *is* useful: + + - It fixes a correctness problem in the template. + - It makes the template cleaner for the next forker (reducing + friction, removing footguns). + - It's in scope for "a generic WhatsApp CRM template" rather + than a bet on your specific workflow. + + If the feature is really for your own deployment, fork and + build it there — see + [CONTRIBUTING.md](https://github.com/ArnasDon/wacrm/blob/main/CONTRIBUTING.md). + + - type: textarea + id: problem + attributes: + label: What's the problem? + description: What are you trying to do today that WaCRM makes harder than it should? + placeholder: | + When a broadcast ends I have no way to see which recipients + didn't reply so I can follow up manually. + validations: + required: true + + - type: textarea + id: proposal + attributes: + label: What would you like to see? + description: Describe the feature from the user's perspective, not the implementation. + placeholder: | + On the broadcast detail page, a "No reply" filter that lists + recipients who received but didn't reply within 24 hours. + validations: + required: true + + - type: textarea + id: alternatives + attributes: + label: Alternatives you considered + description: Things that kinda work today, workarounds, related features in other tools. + validations: + required: false + + - type: dropdown + id: scope + attributes: + label: Scope + description: Your guess — we'll re-scope if needed. + options: + - Small (a couple of hours) + - Medium (a couple of days) + - Large (a couple of weeks, probably breaks into multiple PRs) + validations: + required: false diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..6416ee5 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,53 @@ +version: 2 +updates: + # Runtime + dev dependencies. Weekly on Mondays — keeps the noise + # predictable and aligns with a normal review cycle. + - package-ecosystem: npm + directory: "/" + schedule: + interval: weekly + day: monday + open-pull-requests-limit: 5 + reviewers: + - ArnasDon + labels: + - dependencies + groups: + # Batch related upgrades into one PR so we're not closing six + # independent Supabase-bumps per week. + supabase: + patterns: + - "@supabase/*" + types: + patterns: + - "@types/*" + update-types: + - minor + - patch + dev-dependencies: + dependency-type: development + update-types: + - minor + - patch + ignore: + # Next + React + Tailwind are pinned exactly on purpose. Let + # them flow through manual major bumps when we're ready. + - dependency-name: next + - dependency-name: react + - dependency-name: react-dom + - dependency-name: tailwindcss + - dependency-name: eslint-config-next + + # GitHub Actions in .github/workflows/. Catches setup-node / + # checkout version bumps that keep the CI matrix healthy. + - package-ecosystem: github-actions + directory: "/" + schedule: + interval: weekly + day: monday + open-pull-requests-limit: 3 + reviewers: + - ArnasDon + labels: + - dependencies + - github-actions diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..a978f52 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,42 @@ + + +## Summary + + + +## What changed + + + +## Test plan + + + +- [ ] `npm run typecheck` clean. +- [ ] `npm run lint` — no new errors beyond the pre-existing backlog. +- [ ] `npm run build` succeeds. +- [ ] Feature / fix manually exercised in the browser (or the reason it can't be). + +## Related + + + + diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..1b713ae --- /dev/null +++ b/.prettierignore @@ -0,0 +1,9 @@ +node_modules +.next +out +build +coverage +next-env.d.ts +package-lock.json +# Generated / vendored +supabase/migrations diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..c03564e --- /dev/null +++ b/.prettierrc @@ -0,0 +1,10 @@ +{ + "semi": true, + "singleQuote": true, + "trailingComma": "es5", + "printWidth": 80, + "tabWidth": 2, + "arrowParens": "always", + "endOfLine": "lf", + "plugins": ["prettier-plugin-tailwindcss"] +} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..6814637 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,124 @@ +# Using WaCRM + +WaCRM is a **template repository**, not a collaborative product. The +expected flow is: + +1. **Fork** it to your own GitHub account or organisation. +2. **Deploy** the fork — see [`docs/`](./docs/README.md). +3. **Customise** your fork. Rebrand, add the features you need, remove + the ones you don't, swap hosting, change the schema. + +You **don't** need to send changes back upstream. The fact that your +fork diverges is the whole point — the upstream is deliberately +opinionated about stack, UX, and scope, and your fork is where those +opinions become yours. + +## Fork and run + +```bash +# 1. Fork on GitHub: https://github.com/ArnasDon/wacrm → Fork +# 2. Clone your fork +git clone https://github.com//wacrm.git +cd wacrm + +cp .env.local.example .env.local # fill in Supabase + Meta creds +npm install +npm run dev +``` + +Full setup (Supabase migrations, WhatsApp Business API, deploy) lives in +[`docs/`](./docs/README.md). + +## Keeping your fork up to date + +Pull in upstream bug fixes and security patches periodically: + +```bash +git remote add upstream https://github.com/ArnasDon/wacrm.git # once +git fetch upstream +git checkout main +git merge upstream/main # or: git rebase upstream/main +# Resolve any conflicts (likely in areas you've customised), then push +git push origin main +``` + +If you've made heavy local customisations, rebasing can surface +conflicts every time you pull. Pinning to a specific upstream tag and +updating on your schedule is a valid alternative. + +## Reporting bugs in the upstream template + +If you find a bug in the upstream code — not one you introduced in your +fork — please file it using the +[bug report](https://github.com/ArnasDon/wacrm/issues/new?template=bug_report.yml) +template. Including the commit SHA, the runtime (Hostinger / Vercel / +local / other), and logs will get to a fix fastest. + +## Reporting security issues + +**Do not file security issues publicly.** Follow the private flow in +[SECURITY.md](./.github/SECURITY.md). + +## Upstream pull requests + +Not the primary flow, but welcome in specific cases: + +- **Security fixes** — always welcome, please follow SECURITY.md first + for disclosure. +- **Bug fixes** that match upstream intent (crash, correctness, + documentation errors, typos) — land quickly. +- **Small improvements** (accessibility, obvious UX nits) — usually + welcome, open an issue first to check alignment. + +Less likely to land: + +- **New features.** The template's scope is intentionally narrow. A + "great idea for a CRM" is often a great idea for *your* CRM — i.e. + your fork — but would dilute the template for the next forker. +- **Stack changes** (different ORM, different UI kit, different auth + provider). These belong in a fork, not upstream. +- **Opinionated refactors** without a concrete correctness or + performance motivation. + +If you do send a PR, the usual rules apply: + +- Branch off the latest `main` (don't push to a merged branch — commits + end up orphaned). +- Run `npm run typecheck` and `npm run format` locally first. +- Fill in the PR template, especially the **Test plan**. +- One logical change per PR. +- Commit-message first line is imperative + terse; the body explains + the *why*, the diff shows the *what*. + +Expect a review within a few days. PRs opened without an issue may be +closed — open the issue first to align. + +## If you maintain a public fork + +- Rebrand. The WaCRM name, favicon, and `wacrm.tech` URL belong to the + upstream project; please swap them for your own before putting your + deployment in front of users. +- Keep the MIT [`LICENSE`](./LICENSE) file — that's how the template's + permissions travel with the code. Attribution in a `README` section + is appreciated but not required. +- You are free to re-license additions to your fork however you like. + +## Dev-loop reference + +Even if you never send a PR upstream, these are the scripts you'll use +in your fork: + +| Command | What it does | +| --- | --- | +| `npm run dev` | Turbopack dev server on port 3000. | +| `npm run build` | Production build. Next also runs its own typecheck here. | +| `npm run typecheck` | `tsc --noEmit`. Fast TS-only pass. | +| `npm run lint` | ESLint. | +| `npm run format` | Prettier write. | +| `npm run format:check` | Prettier in check-only mode. Useful in CI. | + +## Licensing + +This template is MIT ([`LICENSE`](./LICENSE)). Anything you contribute +upstream is assumed to be MIT too. Your fork's additions are yours to +license however you like. diff --git a/package-lock.json b/package-lock.json index 04bc56f..721299c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -35,6 +35,8 @@ "@types/react-dom": "^19", "eslint": "^9", "eslint-config-next": "16.2.4", + "prettier": "^3.8.3", + "prettier-plugin-tailwindcss": "^0.7.2", "tailwindcss": "^4", "typescript": "^5" }, @@ -7939,6 +7941,101 @@ "node": ">= 0.8.0" } }, + "node_modules/prettier": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.3.tgz", + "integrity": "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-plugin-tailwindcss": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.7.2.tgz", + "integrity": "sha512-LkphyK3Fw+q2HdMOoiEHWf93fNtYJwfamoKPl7UwtjFQdei/iIBoX11G6j706FzN3ymX9mPVi97qIY8328vdnA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.19" + }, + "peerDependencies": { + "@ianvs/prettier-plugin-sort-imports": "*", + "@prettier/plugin-hermes": "*", + "@prettier/plugin-oxc": "*", + "@prettier/plugin-pug": "*", + "@shopify/prettier-plugin-liquid": "*", + "@trivago/prettier-plugin-sort-imports": "*", + "@zackad/prettier-plugin-twig": "*", + "prettier": "^3.0", + "prettier-plugin-astro": "*", + "prettier-plugin-css-order": "*", + "prettier-plugin-jsdoc": "*", + "prettier-plugin-marko": "*", + "prettier-plugin-multiline-arrays": "*", + "prettier-plugin-organize-attributes": "*", + "prettier-plugin-organize-imports": "*", + "prettier-plugin-sort-imports": "*", + "prettier-plugin-svelte": "*" + }, + "peerDependenciesMeta": { + "@ianvs/prettier-plugin-sort-imports": { + "optional": true + }, + "@prettier/plugin-hermes": { + "optional": true + }, + "@prettier/plugin-oxc": { + "optional": true + }, + "@prettier/plugin-pug": { + "optional": true + }, + "@shopify/prettier-plugin-liquid": { + "optional": true + }, + "@trivago/prettier-plugin-sort-imports": { + "optional": true + }, + "@zackad/prettier-plugin-twig": { + "optional": true + }, + "prettier-plugin-astro": { + "optional": true + }, + "prettier-plugin-css-order": { + "optional": true + }, + "prettier-plugin-jsdoc": { + "optional": true + }, + "prettier-plugin-marko": { + "optional": true + }, + "prettier-plugin-multiline-arrays": { + "optional": true + }, + "prettier-plugin-organize-attributes": { + "optional": true + }, + "prettier-plugin-organize-imports": { + "optional": true + }, + "prettier-plugin-sort-imports": { + "optional": true + }, + "prettier-plugin-svelte": { + "optional": true + } + } + }, "node_modules/pretty-ms": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.3.0.tgz", diff --git a/package.json b/package.json index df41fb2..a742e06 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,9 @@ "build": "next build", "start": "next start", "lint": "eslint", - "typecheck": "tsc --noEmit" + "typecheck": "tsc --noEmit", + "format": "prettier --write .", + "format:check": "prettier --check ." }, "dependencies": { "@base-ui/react": "^1.4.0", @@ -61,6 +63,8 @@ "@types/react-dom": "^19", "eslint": "^9", "eslint-config-next": "16.2.4", + "prettier": "^3.8.3", + "prettier-plugin-tailwindcss": "^0.7.2", "tailwindcss": "^4", "typescript": "^5" }