vault backup: 2026-09-26 11:26:54
This commit is contained in:
@@ -1,2 +1,4 @@
|
||||
|
||||
/.obsidian/workspace.json
|
||||
/.obsidian/plugins/*
|
||||
/.obsidian/workspace.json
|
||||
Vendored
+6
-1
@@ -1 +1,6 @@
|
||||
{}
|
||||
{
|
||||
"cssTheme": "",
|
||||
"accentColor": "#16ac20",
|
||||
"theme": "system",
|
||||
"interfaceFontFamily": ""
|
||||
}
|
||||
Vendored
+2
-1
@@ -1,5 +1,6 @@
|
||||
[
|
||||
"obsidian-git",
|
||||
"tasknotes",
|
||||
"advanced-canvas"
|
||||
"advanced-canvas",
|
||||
"awesome-format-bar"
|
||||
]
|
||||
@@ -1,39 +0,0 @@
|
||||
{
|
||||
"settings": {
|
||||
"migrated": 6,
|
||||
"iconPacksPath": ".obsidian/icons",
|
||||
"fontSize": 16,
|
||||
"emojiStyle": "native",
|
||||
"iconColor": null,
|
||||
"recentlyUsedIcons": [
|
||||
"LiCheck",
|
||||
"LiTasknotesSimple",
|
||||
"LiBookMarked",
|
||||
"LiInfo"
|
||||
],
|
||||
"recentlyUsedIconsSize": 5,
|
||||
"rules": [],
|
||||
"extraMargin": {
|
||||
"top": 0,
|
||||
"right": 4,
|
||||
"bottom": 0,
|
||||
"left": 0
|
||||
},
|
||||
"iconInTabsEnabled": false,
|
||||
"iconInTitleEnabled": false,
|
||||
"iconInTitlePosition": "above",
|
||||
"iconInFrontmatterEnabled": false,
|
||||
"iconInFrontmatterFieldName": "icon",
|
||||
"iconColorInFrontmatterFieldName": "iconColor",
|
||||
"iconsBackgroundCheckEnabled": false,
|
||||
"iconsInNotesEnabled": true,
|
||||
"iconsInLinksEnabled": true,
|
||||
"iconIdentifier": ":",
|
||||
"lucideIconPackType": "native",
|
||||
"debugMode": false,
|
||||
"useInternalPlugins": false
|
||||
},
|
||||
"00 About": "LiInfo",
|
||||
"01 Project docs": "LiBookMarked",
|
||||
"TaskNotes": "LiCheck"
|
||||
}
|
||||
-7158
File diff suppressed because one or more lines are too long
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"id": "obsidian-icon-folder",
|
||||
"name": "Iconize",
|
||||
"version": "2.14.7",
|
||||
"minAppVersion": "0.9.12",
|
||||
"description": "Add icons to anything you desire in Obsidian, including files, folders, and text.",
|
||||
"author": "Florian Woelki",
|
||||
"authorUrl": "https://florianwoelki.com/",
|
||||
"isDesktopOnly": false
|
||||
}
|
||||
-120
@@ -1,120 +0,0 @@
|
||||
.iconize-inline-title-wrapper {
|
||||
width: var(--line-width);
|
||||
max-width: var(--max-width);
|
||||
margin-inline: var(--content-margin);
|
||||
}
|
||||
|
||||
.iconize-title-icon {
|
||||
max-width: var(--max-width);
|
||||
margin-right: var(--size-4-2);
|
||||
}
|
||||
|
||||
.iconize-icon-in-link {
|
||||
transform: translateY(20%);
|
||||
margin-right: var(--size-2-2);
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.iconize-icon {
|
||||
border: 1px solid transparent;
|
||||
margin: 0px 4px 0px 0px;
|
||||
display: flex;
|
||||
align-self: center;
|
||||
margin: auto 0;
|
||||
}
|
||||
|
||||
.nav-folder-title,
|
||||
.nav-file-title {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.iconize-setting input[type='color'] {
|
||||
margin: 0 6px;
|
||||
}
|
||||
|
||||
.iconize-modal.prompt-results {
|
||||
margin: 0;
|
||||
overflow-y: auto;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(5, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.prompt .iconize-subheadline {
|
||||
margin-top: 12px;
|
||||
font-size: 12px;
|
||||
color: gray;
|
||||
grid-column-start: 1;
|
||||
grid-column-end: 6;
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.iconize-modal.prompt-results {
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
}
|
||||
.prompt .iconize-subheadline {
|
||||
grid-column-end: 4;
|
||||
}
|
||||
}
|
||||
|
||||
.iconize-modal.prompt-results .suggestion-item {
|
||||
cursor: pointer;
|
||||
white-space: pre-wrap;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
flex-direction: column-reverse;
|
||||
text-align: center;
|
||||
font-size: 13px;
|
||||
color: var(--text-muted);
|
||||
padding: 16px 8px;
|
||||
line-break: auto;
|
||||
word-break: break-word;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.iconize-modal.prompt-results .suggestion-item.suggestion-item__center {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.iconize-icon-preview {
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.iconize-icon-preview img {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.iconize-icon-preview svg {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
color: currentColor;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.iconize-dragover {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.iconize-dragover-el {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
color: var(--text-normal);
|
||||
background-color: var(--background-secondary-alt);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* Custom rule modal. */
|
||||
.iconize-custom-modal .modal-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.iconize-custom-modal .modal-content input {
|
||||
width: 100%;
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
Vendored
+21
-17
@@ -4,21 +4,24 @@
|
||||
"type": "split",
|
||||
"children": [
|
||||
{
|
||||
"id": "c2be3e9a062c0b56",
|
||||
"id": "6cfedf15c65a27df",
|
||||
"type": "tabs",
|
||||
"children": [
|
||||
{
|
||||
"id": "87c3a13251758aec",
|
||||
"id": "060120ed5e7b1b64",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "split-diff-view",
|
||||
"type": "canvas",
|
||||
"state": {
|
||||
"aFile": ".gitignore",
|
||||
"bFile": ".gitignore",
|
||||
"aRef": ""
|
||||
"file": "00 About/Overview diagram.canvas",
|
||||
"viewState": {
|
||||
"x": -630,
|
||||
"y": -340,
|
||||
"zoom": -0.5081109222000615
|
||||
}
|
||||
},
|
||||
"icon": "diff",
|
||||
"title": "Diff: .gitignore (Working Tree)"
|
||||
"icon": "lucide-layout-dashboard",
|
||||
"title": "Overview diagram"
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -182,34 +185,35 @@
|
||||
}
|
||||
],
|
||||
"direction": "horizontal",
|
||||
"width": 300
|
||||
"width": 323.5
|
||||
},
|
||||
"left-ribbon": {
|
||||
"hiddenItems": {
|
||||
"switcher:Open quick switcher": false,
|
||||
"graph:Open graph view": false,
|
||||
"switcher:Open quick switcher": true,
|
||||
"canvas:Create new canvas": false,
|
||||
"daily-notes:Open today's daily note": false,
|
||||
"daily-notes:Open today's daily note": true,
|
||||
"templates:Insert template": false,
|
||||
"command-palette:Open command palette": false,
|
||||
"bases:Create new base": false,
|
||||
"bases:Create new base": true,
|
||||
"obsidian-git:Open Git source control": false,
|
||||
"tasknotes:Open mini calendar view": true,
|
||||
"tasknotes:Open calendar view": true,
|
||||
"tasknotes:Open tasks view": true,
|
||||
"tasknotes:Open tasks view": false,
|
||||
"tasknotes:Open agenda view": true,
|
||||
"tasknotes:Open Kanban board": false,
|
||||
"graph:Open graph view": false,
|
||||
"tasknotes:Open Pomodoro timer": true,
|
||||
"tasknotes:Open Pomodoro statistics": true,
|
||||
"tasknotes:Create new task": false
|
||||
"tasknotes:Create new task": true
|
||||
}
|
||||
},
|
||||
"active": "87c3a13251758aec",
|
||||
"active": "060120ed5e7b1b64",
|
||||
"lastOpenFiles": [
|
||||
"00 About/Requirements.md",
|
||||
"00 About/Overview diagram.canvas",
|
||||
"TaskNotes/Start Here.md",
|
||||
"Untitled Document",
|
||||
"TaskNotes/Views/kanban-default.base",
|
||||
"TaskNotes/Start Here.md",
|
||||
"TaskNotes/Views/calendar-default.base"
|
||||
]
|
||||
}
|
||||
@@ -51,51 +51,10 @@
|
||||
"styleAttributes":{"textAlign":"center"},
|
||||
"x":-760,
|
||||
"y":-440,
|
||||
"width":180,
|
||||
"width":220,
|
||||
"height":270,
|
||||
"color":"6"
|
||||
},
|
||||
{
|
||||
"id":"0648cf705ffedc6f",
|
||||
"type":"text",
|
||||
"text":"Overview board:\n- pregled nad vsemi naročili\n",
|
||||
"styleAttributes":{"textAlign":"center"},
|
||||
"x":-440,
|
||||
"y":-300,
|
||||
"width":260,
|
||||
"height":100
|
||||
},
|
||||
{
|
||||
"id":"5e0af7712a4944dd",
|
||||
"type":"text",
|
||||
"text":"Admin",
|
||||
"styleAttributes":{"textAlign":"center"},
|
||||
"x":-220,
|
||||
"y":-440,
|
||||
"width":140,
|
||||
"height":60
|
||||
},
|
||||
{
|
||||
"id":"909284a7d8e1f746",
|
||||
"type":"text",
|
||||
"text":"QR code scan\n",
|
||||
"styleAttributes":{"textAlign":"center"},
|
||||
"x":-120,
|
||||
"y":-300,
|
||||
"width":260,
|
||||
"height":100
|
||||
},
|
||||
{
|
||||
"id":"9e2bb4fd16870db1",
|
||||
"type":"text",
|
||||
"text":"Prijava",
|
||||
"styleAttributes":{"textAlign":"center"},
|
||||
"x":-220,
|
||||
"y":-600,
|
||||
"width":140,
|
||||
"height":60,
|
||||
"color":"2"
|
||||
},
|
||||
{
|
||||
"id":"02dbc1e4176bfdb2",
|
||||
"type":"text",
|
||||
@@ -126,6 +85,47 @@
|
||||
"width":220,
|
||||
"height":60,
|
||||
"color":"2"
|
||||
},
|
||||
{
|
||||
"id":"0648cf705ffedc6f",
|
||||
"type":"text",
|
||||
"text":"Overview board:\n- pregled nad vsemi naročili\n",
|
||||
"styleAttributes":{"textAlign":"center"},
|
||||
"x":-440,
|
||||
"y":-300,
|
||||
"width":260,
|
||||
"height":160
|
||||
},
|
||||
{
|
||||
"id":"5e0af7712a4944dd",
|
||||
"type":"text",
|
||||
"text":"Admin",
|
||||
"styleAttributes":{"textAlign":"center"},
|
||||
"x":-220,
|
||||
"y":-440,
|
||||
"width":140,
|
||||
"height":60
|
||||
},
|
||||
{
|
||||
"id":"909284a7d8e1f746",
|
||||
"type":"text",
|
||||
"text":"QR code scan\n",
|
||||
"styleAttributes":{"textAlign":"center"},
|
||||
"x":-120,
|
||||
"y":-300,
|
||||
"width":260,
|
||||
"height":100
|
||||
},
|
||||
{
|
||||
"id":"9e2bb4fd16870db1",
|
||||
"type":"text",
|
||||
"text":"Prijava",
|
||||
"styleAttributes":{"textAlign":"center"},
|
||||
"x":-220,
|
||||
"y":-600,
|
||||
"width":140,
|
||||
"height":60,
|
||||
"color":"2"
|
||||
}
|
||||
],
|
||||
"edges":[
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
### 📱 PERSON A — Web App (Customer-Facing)
|
||||
|
||||
#### Functional Requirements
|
||||
|
||||
- **WEB-FR-1**: User registration (email + password) with email verification
|
||||
- **WEB-FR-2**: Login / logout
|
||||
- **WEB-FR-3**: Password reset flow
|
||||
- **WEB-FR-4**: Display the event's card offering — name, description, price, expiration date
|
||||
- **WEB-FR-5**: Checkout flow via Stripe (EUR)
|
||||
- _Open item: single card per order, or multiple?_
|
||||
- **WEB-FR-6**: On confirmed payment (via Stripe webhook), trigger card/QR generation (calls shared backend)
|
||||
- **WEB-FR-7**: Send confirmation email with QR code attached/embedded (via transactional email service)
|
||||
- **WEB-FR-8**: "My Cards" account page — shows purchase history, card status (unused/used/expired), QR re-download button
|
||||
- **WEB-FR-9**: Basic account settings page (update email/password)
|
||||
|
||||
#### Non-Functional Requirements
|
||||
|
||||
- **WEB-NFR-1**: HTTPS only
|
||||
- **WEB-NFR-2**: GDPR-compliant — privacy policy, consent checkbox at registration, data retention/erasure process
|
||||
- **WEB-NFR-3**: No raw card/payment data stored — Stripe handles PCI compliance, you only store payment references
|
||||
- **WEB-NFR-4**: Responsive design (mobile browser support, since users will pull up emails on phones too, though the QR itself is scanned by the admin's phone, not theirs)
|
||||
|
||||
#### Open Items for This Side
|
||||
|
||||
1. Multi-card orders — yes/no, and if yes, does each card need a separate recipient email field?
|
||||
2. Refund/cancellation flow — in scope for v1 or not?
|
||||
3. Any requirement to resend the QR email if a user loses it (vs. just downloading from account page)?
|
||||
|
||||
---
|
||||
|
||||
### 📷 PERSON B — Android Scanner App (Admin-Facing)
|
||||
|
||||
#### Functional Requirements
|
||||
|
||||
- **APP-FR-1**: Single admin login (one hardcoded/managed admin role — no multi-account complexity)
|
||||
- **APP-FR-2**: Camera-based QR scanning (use a standard library — e.g., ML Kit or ZXing)
|
||||
- **APP-FR-3**: On scan, send token to backend validation endpoint
|
||||
- **APP-FR-4**: Display result clearly and immediately:
|
||||
- ✅ **Valid** → show card owner name + card type, mark as used
|
||||
- ❌ **Already Used** → show when it was originally used
|
||||
- ❌ **Expired** → show expiration date
|
||||
- ❌ **Not Found** → invalid/forged code
|
||||
- **APP-FR-5**: Local on-device scan log (timestamp + result) for admin's own reference during the event
|
||||
- **APP-FR-6**: Manual entry fallback (in case camera/QR fails to scan — type in code manually)
|
||||
|
||||
#### Non-Functional Requirements
|
||||
|
||||
- **APP-NFR-1**: Requires internet connectivity to validate (confirm: acceptable, or do you need offline fallback?)
|
||||
- **APP-NFR-2**: Fast response time — scan-to-result should feel instant (sub-1s ideally) since there may be a line of people
|
||||
- **APP-NFR-3**: Simple, high-contrast UI readable in bright outdoor daylight or dark event lighting
|
||||
- **APP-NFR-4**: Since it's single-admin/single-device, no need to handle concurrent scan conflicts — backend validation is still atomic, but app itself doesn't need sync logic
|
||||
|
||||
#### Open Items for This Side
|
||||
|
||||
1. **Offline fallback** — if there's no signal at the venue, what happens? Worth deciding now since it affects architecture (native online-only call vs. local cache + sync).
|
||||
2. Does the admin need to _undo_ an accidental valid scan (e.g., mis-scan, wrong card)?
|
||||
3. Any requirement to see a running count (e.g., "142 checked in") during the event?
|
||||
|
||||
---
|
||||
|
||||
### 🔗 SHARED — Backend Contract (Both of you depend on this)
|
||||
|
||||
This is the glue between your two apps — worth agreeing on before you start building independently, since a mismatch here is the most likely source of integration pain later.
|
||||
|
||||
- **API-1**: `POST /api/orders` — creates order, integrates Stripe checkout
|
||||
- **API-2**: Stripe webhook handler — on payment success, generates Card record + signed QR token, triggers email
|
||||
- **API-3**: `POST /api/cards/validate` — takes scanned token, returns `{status: valid|used|expired|not_found, owner_name, card_type, used_at?}`, atomically updates status if valid
|
||||
- **API-4**: `GET /api/users/me/cards` — for the web app's "My Cards" page
|
||||
- **API-5**: Shared **Card** data model: `id, unique_token (signed), user_id, card_type, status, purchased_at, expires_at, used_at`
|
||||
- **API-6**: Auth — separate token/role for admin scanner vs. customer web sessions
|
||||
|
||||
**Recommendation:** whoever owns the backend (or whoever's building it jointly) should nail down the exact JSON shape of `API-3`'s response first, since that's the contract Person B's UI directly renders.
|
||||
Reference in New Issue
Block a user