Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9636be78c9 | ||
|
|
a50ca5c171 | ||
|
|
e271702bca | ||
|
|
d3d76b1d0d | ||
|
|
8cae0665db | ||
|
|
21453aab7d | ||
|
|
691186e02c | ||
|
|
fd48aaab4d | ||
|
|
0b56d0d6c4 | ||
|
|
0d22998c2e |
21
LICENSE
Normal file
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2026 Steven Jannette
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
17
README.md
17
README.md
@@ -1,10 +1,10 @@
|
|||||||
# kongruity
|
# kongruity
|
||||||
|
|
||||||
kongruity pulls in the unstructured artifacts of the creative-engineering process -- to-dos, action items, agile tickets, Jira comment threads, Slack threads, retrospective notes -- and synthesizes them into semantically coherent, prioritized clusters ready for implementation planning.
|
kongruity pulls in unstructured artifacts of the creative-engineering process -- to-dos, action items, agile tickets, Jira thread comments, Slack thread comments, retrospective notes -- and synthesizes them into semantically coherent, prioritized clusters that can be incorporated into implementation planning.
|
||||||
|
|
||||||
In kongruity world, these artifacts are "sticky notes." A board full of them looks chaotic. With a click, an LLM analyzes their semantic meaning and groups them into thematic clusters, each with a descriptive header.
|
In kongruity world, the artifacts become "sticky notes." A board full of them looks chaotic. With a click, the LLM analyzes their semantic meaning and groups them into thematic clusters, each with a descriptive header.
|
||||||
|
|
||||||
An independent embedding-based evaluation scores clustering quality, so the output is data-backed. From there, teams can simply drag-and-rank clusters by implementation priority, turning noise into an actionable workflow.
|
An independent embedding-based evaluation model scores clustering quality, so the output is qualitatively refined. From there, teams can drag-and-rank related task clusters by implementation priority - turning noise into an actionable workflow.
|
||||||
|
|
||||||
## How it works
|
## How it works
|
||||||
|
|
||||||
@@ -14,9 +14,14 @@ An independent embedding-based evaluation scores clustering quality, so the outp
|
|||||||
4. **Validate** — Structural checks confirm every note is assigned to exactly one cluster, no clusters are empty, and labels are present.
|
4. **Validate** — Structural checks confirm every note is assigned to exactly one cluster, no clusters are empty, and labels are present.
|
||||||
5. **Prioritize** — Clusters appear ranked and are drag-reorderable. Teams set implementation priority by dragging clusters into position.
|
5. **Prioritize** — Clusters appear ranked and are drag-reorderable. Teams set implementation priority by dragging clusters into position.
|
||||||
|
|
||||||
## Dev implementation note
|
## Dev implementation notes
|
||||||
|
|
||||||
Developers may swap in other LLM SDKs/APIs and alter prompt syntax in `backend/services/clustering.service.js` to experiment with any model or platform.
|
As of 03.04.2026, two of the above-described features are in the planning and implementation phase:
|
||||||
|
|
||||||
|
1. **Ingestion** - The implementation goal is a system for easily tagging items/issues mentioned in Slack, Jira comments, etc. (similar to hashtagging), and running batch "pulls" of these tagged items into kongruity via REST API interface.
|
||||||
|
2. **Prioritization** -- The final prioritization and planning stage will ultimately result in "pushing" these ordered items back **out** into Jira, Asana, Rally (etc.), for incorporation into Epic/Sprint workflows.
|
||||||
|
|
||||||
|
Also, developers may swap in other LLM SDKs/APIs and alter prompt syntax in `backend/services/clustering.service.js` to experiment with any model or platform of their choice.
|
||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
@@ -144,3 +149,5 @@ This runs Vitest with jsdom. For watch mode during development:
|
|||||||
```bash
|
```bash
|
||||||
npm run test:watch
|
npm run test:watch
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,402 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"id": "note_038",
|
|
||||||
"text": "Live follow mode frequently breaks",
|
|
||||||
"x": 710,
|
|
||||||
"y": 579,
|
|
||||||
"author": "user_9",
|
|
||||||
"color": "yellow"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "note_005",
|
|
||||||
"text": "SSO login loops back to the sign-in page",
|
|
||||||
"x": 184,
|
|
||||||
"y": 124,
|
|
||||||
"author": "user_7",
|
|
||||||
"color": "yellow"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "note_023",
|
|
||||||
"text": "Undo/redo sometimes lags and applies late",
|
|
||||||
"x": 236,
|
|
||||||
"y": 687,
|
|
||||||
"author": "user_9",
|
|
||||||
"color": "purple"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "note_017",
|
|
||||||
"text": "Exported file names are inconsistent and hard to track",
|
|
||||||
"x": 676,
|
|
||||||
"y": 201,
|
|
||||||
"author": "user_4",
|
|
||||||
"color": "blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "note_049",
|
|
||||||
"text": "Need better controls for aligning and distributing notes",
|
|
||||||
"x": 462,
|
|
||||||
"y": 487,
|
|
||||||
"author": "user_7",
|
|
||||||
"color": "green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "note_010",
|
|
||||||
"text": "OAuth consent screen appears every time I sign in",
|
|
||||||
"x": 228,
|
|
||||||
"y": 124,
|
|
||||||
"author": "user_9",
|
|
||||||
"color": "yellow"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "note_036",
|
|
||||||
"text": "Cursor presence is distracting and overlaps content",
|
|
||||||
"x": 788,
|
|
||||||
"y": 605,
|
|
||||||
"author": "user_5",
|
|
||||||
"color": "yellow"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "note_011",
|
|
||||||
"text": "Need better export options (PDF quality is too low)",
|
|
||||||
"x": 748,
|
|
||||||
"y": 212,
|
|
||||||
"author": "user_9",
|
|
||||||
"color": "green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "note_012",
|
|
||||||
"text": "Exported PDF cuts off content near the edges",
|
|
||||||
"x": 733,
|
|
||||||
"y": 159,
|
|
||||||
"author": "user_6",
|
|
||||||
"color": "blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "note_019",
|
|
||||||
"text": "Embedded exports don’t update when the board changes",
|
|
||||||
"x": 662,
|
|
||||||
"y": 132,
|
|
||||||
"author": "user_1",
|
|
||||||
"color": "blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "note_020",
|
|
||||||
"text": "Can’t export comments and reactions with the content",
|
|
||||||
"x": 739,
|
|
||||||
"y": 139,
|
|
||||||
"author": "user_7",
|
|
||||||
"color": "green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "note_039",
|
|
||||||
"text": "Guest collaborators can’t see updates without refreshing",
|
|
||||||
"x": 759,
|
|
||||||
"y": 711,
|
|
||||||
"author": "user_9",
|
|
||||||
"color": "yellow"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "note_024",
|
|
||||||
"text": "App crashes when opening a large board",
|
|
||||||
"x": 239,
|
|
||||||
"y": 597,
|
|
||||||
"author": "user_10",
|
|
||||||
"color": "purple"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "note_041",
|
|
||||||
"text": "Hard to find the right template quickly",
|
|
||||||
"x": 536,
|
|
||||||
"y": 394,
|
|
||||||
"author": "user_4",
|
|
||||||
"color": "orange"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "note_040",
|
|
||||||
"text": "Activity feed lacks context about what changed",
|
|
||||||
"x": 710,
|
|
||||||
"y": 771,
|
|
||||||
"author": "user_7",
|
|
||||||
"color": "yellow"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "note_014",
|
|
||||||
"text": "Export takes too long and sometimes never finishes",
|
|
||||||
"x": 798,
|
|
||||||
"y": 211,
|
|
||||||
"author": "user_2",
|
|
||||||
"color": "green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "note_031",
|
|
||||||
"text": "Hard to tell who is editing what in real time",
|
|
||||||
"x": 761,
|
|
||||||
"y": 704,
|
|
||||||
"author": "user_8",
|
|
||||||
"color": "yellow"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "note_032",
|
|
||||||
"text": "Comments get lost — no clear thread view",
|
|
||||||
"x": 818,
|
|
||||||
"y": 641,
|
|
||||||
"author": "user_5",
|
|
||||||
"color": "yellow"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "note_050",
|
|
||||||
"text": "Can’t lock sections to prevent accidental edits during workshops",
|
|
||||||
"x": 521,
|
|
||||||
"y": 471,
|
|
||||||
"author": "user_6",
|
|
||||||
"color": "orange"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "note_001",
|
|
||||||
"text": "Login flow feels confusing",
|
|
||||||
"x": 193,
|
|
||||||
"y": 191,
|
|
||||||
"author": "user_5",
|
|
||||||
"color": "yellow"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "note_025",
|
|
||||||
"text": "Saving indicator spins but changes aren’t saved",
|
|
||||||
"x": 129,
|
|
||||||
"y": 781,
|
|
||||||
"author": "user_3",
|
|
||||||
"color": "purple"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "note_021",
|
|
||||||
"text": "Board feels slow when there are many sticky notes",
|
|
||||||
"x": 341,
|
|
||||||
"y": 695,
|
|
||||||
"author": "user_10",
|
|
||||||
"color": "purple"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "note_002",
|
|
||||||
"text": "Login flow is broken on mobile",
|
|
||||||
"x": 214,
|
|
||||||
"y": 281,
|
|
||||||
"author": "user_9",
|
|
||||||
"color": "yellow"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "note_046",
|
|
||||||
"text": "Tags are missing — I need better categorization",
|
|
||||||
"x": 472,
|
|
||||||
"y": 435,
|
|
||||||
"author": "user_6",
|
|
||||||
"color": "green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "note_018",
|
|
||||||
"text": "No way to schedule recurring exports for stakeholders",
|
|
||||||
"x": 661,
|
|
||||||
"y": 229,
|
|
||||||
"author": "user_9",
|
|
||||||
"color": "blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "note_034",
|
|
||||||
"text": "Too many notification emails for minor edits",
|
|
||||||
"x": 769,
|
|
||||||
"y": 739,
|
|
||||||
"author": "user_9",
|
|
||||||
"color": "yellow"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "note_003",
|
|
||||||
"text": "When I enter my username and password I get an unknown error",
|
|
||||||
"x": 189,
|
|
||||||
"y": 193,
|
|
||||||
"author": "user_2",
|
|
||||||
"color": "yellow"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "note_008",
|
|
||||||
"text": "Cannot change my password — save button does nothing",
|
|
||||||
"x": 244,
|
|
||||||
"y": 188,
|
|
||||||
"author": "user_2",
|
|
||||||
"color": "orange"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "note_015",
|
|
||||||
"text": "Sharing link permissions are confusing",
|
|
||||||
"x": 688,
|
|
||||||
"y": 290,
|
|
||||||
"author": "user_6",
|
|
||||||
"color": "blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "note_033",
|
|
||||||
"text": "Mentions (@) don’t notify the right people",
|
|
||||||
"x": 696,
|
|
||||||
"y": 669,
|
|
||||||
"author": "user_5",
|
|
||||||
"color": "yellow"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "note_006",
|
|
||||||
"text": "Two-factor code is rejected even when correct",
|
|
||||||
"x": 162,
|
|
||||||
"y": 213,
|
|
||||||
"author": "user_1",
|
|
||||||
"color": "yellow"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "note_028",
|
|
||||||
"text": "High CPU usage even when idle on a board",
|
|
||||||
"x": 190,
|
|
||||||
"y": 695,
|
|
||||||
"author": "user_8",
|
|
||||||
"color": "purple"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "note_009",
|
|
||||||
"text": "Account gets locked too quickly after one failed attempt",
|
|
||||||
"x": 280,
|
|
||||||
"y": 255,
|
|
||||||
"author": "user_10",
|
|
||||||
"color": "orange"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "note_029",
|
|
||||||
"text": "Offline mode loses edits when reconnecting",
|
|
||||||
"x": 338,
|
|
||||||
"y": 632,
|
|
||||||
"author": "user_1",
|
|
||||||
"color": "pink"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "note_035",
|
|
||||||
"text": "No notification when someone resolves my comment",
|
|
||||||
"x": 673,
|
|
||||||
"y": 636,
|
|
||||||
"author": "user_2",
|
|
||||||
"color": "blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "note_037",
|
|
||||||
"text": "Can’t easily hand off facilitation to another user",
|
|
||||||
"x": 816,
|
|
||||||
"y": 707,
|
|
||||||
"author": "user_2",
|
|
||||||
"color": "yellow"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "note_013",
|
|
||||||
"text": "Cannot export selected area — only full board exports",
|
|
||||||
"x": 696,
|
|
||||||
"y": 205,
|
|
||||||
"author": "user_4",
|
|
||||||
"color": "green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "note_016",
|
|
||||||
"text": "Downloaded image is blurry compared to the canvas",
|
|
||||||
"x": 677,
|
|
||||||
"y": 245,
|
|
||||||
"author": "user_5",
|
|
||||||
"color": "blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "note_043",
|
|
||||||
"text": "Can’t organize boards into folders the way I need",
|
|
||||||
"x": 504,
|
|
||||||
"y": 398,
|
|
||||||
"author": "user_4",
|
|
||||||
"color": "green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "note_022",
|
|
||||||
"text": "Canvas freezes for a few seconds when zooming",
|
|
||||||
"x": 254,
|
|
||||||
"y": 707,
|
|
||||||
"author": "user_8",
|
|
||||||
"color": "pink"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "note_026",
|
|
||||||
"text": "Search is slow on boards with lots of content",
|
|
||||||
"x": 253,
|
|
||||||
"y": 658,
|
|
||||||
"author": "user_2",
|
|
||||||
"color": "pink"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "note_048",
|
|
||||||
"text": "Duplicating a board loses some formatting",
|
|
||||||
"x": 382,
|
|
||||||
"y": 410,
|
|
||||||
"author": "user_10",
|
|
||||||
"color": "orange"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "note_007",
|
|
||||||
"text": "I’m logged out unexpectedly after a few minutes",
|
|
||||||
"x": 185,
|
|
||||||
"y": 157,
|
|
||||||
"author": "user_3",
|
|
||||||
"color": "yellow"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "note_045",
|
|
||||||
"text": "No bulk rename for multiple sticky notes",
|
|
||||||
"x": 455,
|
|
||||||
"y": 427,
|
|
||||||
"author": "user_1",
|
|
||||||
"color": "green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "note_042",
|
|
||||||
"text": "Template search results feel irrelevant",
|
|
||||||
"x": 400,
|
|
||||||
"y": 474,
|
|
||||||
"author": "user_6",
|
|
||||||
"color": "orange"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "note_044",
|
|
||||||
"text": "Naming conventions aren’t enforced and things get messy",
|
|
||||||
"x": 469,
|
|
||||||
"y": 434,
|
|
||||||
"author": "user_9",
|
|
||||||
"color": "green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "note_004",
|
|
||||||
"text": "Password reset email never arrives",
|
|
||||||
"x": 207,
|
|
||||||
"y": 267,
|
|
||||||
"author": "user_9",
|
|
||||||
"color": "yellow"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "note_047",
|
|
||||||
"text": "Hard to keep consistent styles across boards",
|
|
||||||
"x": 420,
|
|
||||||
"y": 426,
|
|
||||||
"author": "user_8",
|
|
||||||
"color": "green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "note_030",
|
|
||||||
"text": "I see random 'something went wrong' banners with no details",
|
|
||||||
"x": 214,
|
|
||||||
"y": 594,
|
|
||||||
"author": "user_5",
|
|
||||||
"color": "purple"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "note_027",
|
|
||||||
"text": "Scrolling stutters on older laptops",
|
|
||||||
"x": 178,
|
|
||||||
"y": 586,
|
|
||||||
"author": "user_7",
|
|
||||||
"color": "pink"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
131
backend/db/README.md
Normal file
131
backend/db/README.md
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
# Database — kongruity backend
|
||||||
|
|
||||||
|
PostgreSQL database layer for the kongruity backend. All database modules live in this directory.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- PostgreSQL v14 or later
|
||||||
|
- The `DATABASE_URL` environment variable set in `backend/.env`
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
### Environment variable
|
||||||
|
|
||||||
|
The connection pool reads a single env var:
|
||||||
|
|
||||||
|
```
|
||||||
|
DATABASE_URL=postgresql://<user>:<password>@localhost:5432/kongruity
|
||||||
|
```
|
||||||
|
|
||||||
|
This is loaded via `dotenv` from `backend/.env` (git-ignored). The pool is created once in `index.js` and shared across the application.
|
||||||
|
|
||||||
|
### Connection pool (`index.js`)
|
||||||
|
|
||||||
|
| Export | Description |
|
||||||
|
|-------------|--------------------------------------------------|
|
||||||
|
| `query` | Execute a parameterized SQL statement |
|
||||||
|
| `getPool` | Return the underlying `pg.Pool` instance |
|
||||||
|
| `close` | Gracefully shut down the pool (`pool.end()`) |
|
||||||
|
| `default` | The pool itself (default export) |
|
||||||
|
|
||||||
|
## Schema
|
||||||
|
|
||||||
|
### `notes` table
|
||||||
|
|
||||||
|
Created by the migration in `migrate.js`. The DDL is idempotent (`CREATE TABLE IF NOT EXISTS`).
|
||||||
|
|
||||||
|
| Column | Type | Constraints / Defaults |
|
||||||
|
|---------------|----------------|------------------------------|
|
||||||
|
| `id` | `VARCHAR(64)` | `PRIMARY KEY` |
|
||||||
|
| `text` | `TEXT` | `NOT NULL` |
|
||||||
|
| `x` | `INTEGER` | `DEFAULT 0` |
|
||||||
|
| `y` | `INTEGER` | `DEFAULT 0` |
|
||||||
|
| `author` | `VARCHAR(128)` | |
|
||||||
|
| `color` | `VARCHAR(32)` | `DEFAULT 'yellow'` |
|
||||||
|
| `source_meta` | `JSONB` | `DEFAULT '{}'` |
|
||||||
|
| `created_at` | `TIMESTAMPTZ` | `DEFAULT NOW()` |
|
||||||
|
| `updated_at` | `TIMESTAMPTZ` | `DEFAULT NOW()` |
|
||||||
|
|
||||||
|
## npm scripts
|
||||||
|
|
||||||
|
Run these from the `backend/` directory.
|
||||||
|
|
||||||
|
### Migrate
|
||||||
|
|
||||||
|
Creates the `notes` table (safe to re-run):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run db:migrate
|
||||||
|
```
|
||||||
|
|
||||||
|
### Seed
|
||||||
|
|
||||||
|
Inserts 50 sample sticky notes. Uses `ON CONFLICT (id) DO NOTHING`, so re-running is safe and will not duplicate data:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run db:seed
|
||||||
|
```
|
||||||
|
|
||||||
|
## Data access layer (`notes.dao.js`)
|
||||||
|
|
||||||
|
| Function | Description |
|
||||||
|
|---------------------------|--------------------------------------------------------------------|
|
||||||
|
| `getAllNotes()` | Returns all notes ordered by `id` |
|
||||||
|
| `getNoteById(id)` | Returns a single note by primary key, or `null` if not found |
|
||||||
|
| `createNote(note)` | Inserts one note and returns the created row |
|
||||||
|
| `createNotes(notes)` | Bulk-inserts an array of notes in a single query and returns rows |
|
||||||
|
|
||||||
|
All functions return plain objects with columns: `id`, `text`, `x`, `y`, `author`, `color`.
|
||||||
|
|
||||||
|
## File overview
|
||||||
|
|
||||||
|
```
|
||||||
|
db/
|
||||||
|
├── index.js # Connection pool and query helper
|
||||||
|
├── migrate.js # Table creation (run via npm run db:migrate)
|
||||||
|
├── notes.dao.js # Data access functions for the notes table
|
||||||
|
├── seed.js # Sample data seeder (run via npm run db:seed)
|
||||||
|
└── README.md # This file
|
||||||
|
```
|
||||||
|
|
||||||
|
## Useful psql commands
|
||||||
|
|
||||||
|
Connect to the database:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
psql kongruity
|
||||||
|
```
|
||||||
|
|
||||||
|
Quick checks:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- Row count
|
||||||
|
SELECT count(*) FROM notes;
|
||||||
|
|
||||||
|
-- Preview data
|
||||||
|
SELECT id, text, color FROM notes LIMIT 10;
|
||||||
|
|
||||||
|
-- Full schema info
|
||||||
|
\d notes
|
||||||
|
|
||||||
|
-- Drop and re-seed (destructive)
|
||||||
|
TRUNCATE notes;
|
||||||
|
```
|
||||||
|
|
||||||
|
Then re-seed:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run db:seed
|
||||||
|
```
|
||||||
|
|
||||||
|
## Resetting the database
|
||||||
|
|
||||||
|
To start completely fresh:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
dropdb kongruity
|
||||||
|
createdb kongruity
|
||||||
|
cd backend
|
||||||
|
npm run db:migrate
|
||||||
|
npm run db:seed
|
||||||
|
```
|
||||||
@@ -1,14 +1,61 @@
|
|||||||
import 'dotenv/config';
|
import 'dotenv/config';
|
||||||
import { readFile } from 'fs/promises';
|
|
||||||
import { query, close } from './index.js';
|
import { query, close } from './index.js';
|
||||||
|
|
||||||
const NOTES_PATH = new URL('../data/notes.json', import.meta.url);
|
const notes = [
|
||||||
|
{ id: 'note_001', text: 'Login flow feels confusing', x: 193, y: 191, author: 'user_5', color: 'yellow' },
|
||||||
|
{ id: 'note_002', text: 'Login flow is broken on mobile', x: 214, y: 281, author: 'user_9', color: 'yellow' },
|
||||||
|
{ id: 'note_003', text: 'When I enter my username and password I get an unknown error', x: 189, y: 193, author: 'user_2', color: 'yellow' },
|
||||||
|
{ id: 'note_004', text: 'Password reset email never arrives', x: 207, y: 267, author: 'user_9', color: 'yellow' },
|
||||||
|
{ id: 'note_005', text: 'SSO login loops back to the sign-in page', x: 184, y: 124, author: 'user_7', color: 'yellow' },
|
||||||
|
{ id: 'note_006', text: 'Two-factor code is rejected even when correct', x: 162, y: 213, author: 'user_1', color: 'yellow' },
|
||||||
|
{ id: 'note_007', text: "I'm logged out unexpectedly after a few minutes", x: 185, y: 157, author: 'user_3', color: 'yellow' },
|
||||||
|
{ id: 'note_008', text: 'Cannot change my password — save button does nothing', x: 244, y: 188, author: 'user_2', color: 'orange' },
|
||||||
|
{ id: 'note_009', text: 'Account gets locked too quickly after one failed attempt', x: 280, y: 255, author: 'user_10', color: 'orange' },
|
||||||
|
{ id: 'note_010', text: 'OAuth consent screen appears every time I sign in', x: 228, y: 124, author: 'user_9', color: 'yellow' },
|
||||||
|
{ id: 'note_011', text: 'Need better export options (PDF quality is too low)', x: 748, y: 212, author: 'user_9', color: 'green' },
|
||||||
|
{ id: 'note_012', text: 'Exported PDF cuts off content near the edges', x: 733, y: 159, author: 'user_6', color: 'blue' },
|
||||||
|
{ id: 'note_013', text: 'Cannot export selected area — only full board exports', x: 696, y: 205, author: 'user_4', color: 'green' },
|
||||||
|
{ id: 'note_014', text: 'Export takes too long and sometimes never finishes', x: 798, y: 211, author: 'user_2', color: 'green' },
|
||||||
|
{ id: 'note_015', text: 'Sharing link permissions are confusing', x: 688, y: 290, author: 'user_6', color: 'blue' },
|
||||||
|
{ id: 'note_016', text: 'Downloaded image is blurry compared to the canvas', x: 677, y: 245, author: 'user_5', color: 'blue' },
|
||||||
|
{ id: 'note_017', text: 'Exported file names are inconsistent and hard to track', x: 676, y: 201, author: 'user_4', color: 'blue' },
|
||||||
|
{ id: 'note_018', text: 'No way to schedule recurring exports for stakeholders', x: 661, y: 229, author: 'user_9', color: 'blue' },
|
||||||
|
{ id: 'note_019', text: "Embedded exports don't update when the board changes", x: 662, y: 132, author: 'user_1', color: 'blue' },
|
||||||
|
{ id: 'note_020', text: "Can't export comments and reactions with the content", x: 739, y: 139, author: 'user_7', color: 'green' },
|
||||||
|
{ id: 'note_021', text: 'Board feels slow when there are many sticky notes', x: 341, y: 695, author: 'user_10', color: 'purple' },
|
||||||
|
{ id: 'note_022', text: 'Canvas freezes for a few seconds when zooming', x: 254, y: 707, author: 'user_8', color: 'pink' },
|
||||||
|
{ id: 'note_023', text: 'Undo/redo sometimes lags and applies late', x: 236, y: 687, author: 'user_9', color: 'purple' },
|
||||||
|
{ id: 'note_024', text: 'App crashes when opening a large board', x: 239, y: 597, author: 'user_10', color: 'purple' },
|
||||||
|
{ id: 'note_025', text: "Saving indicator spins but changes aren't saved", x: 129, y: 781, author: 'user_3', color: 'purple' },
|
||||||
|
{ id: 'note_026', text: 'Search is slow on boards with lots of content', x: 253, y: 658, author: 'user_2', color: 'pink' },
|
||||||
|
{ id: 'note_027', text: 'Scrolling stutters on older laptops', x: 178, y: 586, author: 'user_7', color: 'pink' },
|
||||||
|
{ id: 'note_028', text: 'High CPU usage even when idle on a board', x: 190, y: 695, author: 'user_8', color: 'purple' },
|
||||||
|
{ id: 'note_029', text: 'Offline mode loses edits when reconnecting', x: 338, y: 632, author: 'user_1', color: 'pink' },
|
||||||
|
{ id: 'note_030', text: "I see random 'something went wrong' banners with no details", x: 214, y: 594, author: 'user_5', color: 'purple' },
|
||||||
|
{ id: 'note_031', text: 'Hard to tell who is editing what in real time', x: 761, y: 704, author: 'user_8', color: 'yellow' },
|
||||||
|
{ id: 'note_032', text: 'Comments get lost — no clear thread view', x: 818, y: 641, author: 'user_5', color: 'yellow' },
|
||||||
|
{ id: 'note_033', text: "Mentions (@) don't notify the right people", x: 696, y: 669, author: 'user_5', color: 'yellow' },
|
||||||
|
{ id: 'note_034', text: 'Too many notification emails for minor edits', x: 769, y: 739, author: 'user_9', color: 'yellow' },
|
||||||
|
{ id: 'note_035', text: 'No notification when someone resolves my comment', x: 673, y: 636, author: 'user_2', color: 'blue' },
|
||||||
|
{ id: 'note_036', text: 'Cursor presence is distracting and overlaps content', x: 788, y: 605, author: 'user_5', color: 'yellow' },
|
||||||
|
{ id: 'note_037', text: "Can't easily hand off facilitation to another user", x: 816, y: 707, author: 'user_2', color: 'yellow' },
|
||||||
|
{ id: 'note_038', text: 'Live follow mode frequently breaks', x: 710, y: 579, author: 'user_9', color: 'yellow' },
|
||||||
|
{ id: 'note_039', text: "Guest collaborators can't see updates without refreshing", x: 759, y: 711, author: 'user_9', color: 'yellow' },
|
||||||
|
{ id: 'note_040', text: 'Activity feed lacks context about what changed', x: 710, y: 771, author: 'user_7', color: 'yellow' },
|
||||||
|
{ id: 'note_041', text: 'Hard to find the right template quickly', x: 536, y: 394, author: 'user_4', color: 'orange' },
|
||||||
|
{ id: 'note_042', text: 'Template search results feel irrelevant', x: 400, y: 474, author: 'user_6', color: 'orange' },
|
||||||
|
{ id: 'note_043', text: "Can't organize boards into folders the way I need", x: 504, y: 398, author: 'user_4', color: 'green' },
|
||||||
|
{ id: 'note_044', text: "Naming conventions aren't enforced and things get messy", x: 469, y: 434, author: 'user_9', color: 'green' },
|
||||||
|
{ id: 'note_045', text: 'No bulk rename for multiple sticky notes', x: 455, y: 427, author: 'user_1', color: 'green' },
|
||||||
|
{ id: 'note_046', text: 'Tags are missing — I need better categorization', x: 472, y: 435, author: 'user_6', color: 'green' },
|
||||||
|
{ id: 'note_047', text: 'Hard to keep consistent styles across boards', x: 420, y: 426, author: 'user_8', color: 'green' },
|
||||||
|
{ id: 'note_048', text: 'Duplicating a board loses some formatting', x: 382, y: 410, author: 'user_10', color: 'orange' },
|
||||||
|
{ id: 'note_049', text: 'Need better controls for aligning and distributing notes', x: 462, y: 487, author: 'user_7', color: 'green' },
|
||||||
|
{ id: 'note_050', text: "Can't lock sections to prevent accidental edits during workshops", x: 521, y: 471, author: 'user_6', color: 'orange' },
|
||||||
|
];
|
||||||
|
|
||||||
const run = async () => {
|
const run = async () => {
|
||||||
try {
|
try {
|
||||||
const raw = await readFile(NOTES_PATH, 'utf-8');
|
|
||||||
const notes = JSON.parse(raw);
|
|
||||||
|
|
||||||
const insertQuery = `
|
const insertQuery = `
|
||||||
INSERT INTO notes (id, text, x, y, author, color)
|
INSERT INTO notes (id, text, x, y, author, color)
|
||||||
VALUES ($1, $2, $3, $4, $5, $6)
|
VALUES ($1, $2, $3, $4, $5, $6)
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export const embedNotes = async (notes) => {
|
|||||||
|
|
||||||
const response = await client.embed({
|
const response = await client.embed({
|
||||||
input: texts,
|
input: texts,
|
||||||
model: "voyage-3-lite",
|
model: "voyage-3",
|
||||||
});
|
});
|
||||||
|
|
||||||
const embeddingMap = new Map();
|
const embeddingMap = new Map();
|
||||||
|
|||||||
@@ -109,6 +109,9 @@ const Stickies = () => {
|
|||||||
<span className="cluster-rank" aria-label={`Priority ${group.rank}`}>
|
<span className="cluster-rank" aria-label={`Priority ${group.rank}`}>
|
||||||
{group.rank}
|
{group.rank}
|
||||||
</span>
|
</span>
|
||||||
|
{group.rank === 1 && (
|
||||||
|
<span className="cluster-reorder-hint">Drag and drop to reorganize cluster priority</span>
|
||||||
|
)}
|
||||||
<h3 className="cluster-label">{group.label}</h3>
|
<h3 className="cluster-label">{group.label}</h3>
|
||||||
<span className="cluster-drag-handle" aria-hidden="true">⠿</span>
|
<span className="cluster-drag-handle" aria-hidden="true">⠿</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -60,6 +60,14 @@
|
|||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cluster-reorder-hint {
|
||||||
|
font-size: 0.8em;
|
||||||
|
color: #9ca3af;
|
||||||
|
font-style: italic;
|
||||||
|
white-space: nowrap;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.cluster-label {
|
.cluster-label {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-size: 1.2em;
|
font-size: 1.2em;
|
||||||
|
|||||||
Reference in New Issue
Block a user