Commit Graph

211 Commits

Author SHA1 Message Date
Ruslan Bakiev
0f87586e81 fix: OUT messages no longer create unread status + handle Telegram read receipts
Only inbound (IN) messages determine hasUnread in getContacts(). Telegram
read_business_message events are now parsed and processed to auto-mark
contacts as read for the entire team.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 14:53:55 +07:00
Ruslan Bakiev
6291797bb6 chore: upgrade Prisma 7, LangChain 1.x, Tailwind 4.2, Vue 3.5.29 and other deps
- Prisma 6 → 7: new prisma-client generator, prisma.config.ts, PrismaPg adapter, updated all imports
- LangChain 0.x → 1.x: @langchain/core, langgraph, openai
- Tailwind 4.1 → 4.2.1, daisyUI 5.5.19, Vue 3.5.29, ai 6.0.99, zod 4.3.6
- Fix MessageDirection bug in crm-updates.ts (OUTBOUND → OUT)
- Add server/generated to .gitignore

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 09:27:26 +07:00
Ruslan Bakiev
f4891e6932 fix: replace prisma.$use with $extends for Prisma 6 compatibility
prisma.$use middleware API was removed in Prisma 6. Switched to
$extends query hooks which is the supported approach.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 09:08:20 +07:00
Ruslan Bakiev
5c29cde13d refactor: remove manual upsertClientTimelineEntry from GraphQL resolvers
CalendarEvent and FeedCard timeline entries are now handled by Prisma
middleware automatically. Document timeline entry is inlined since
WorkspaceDocument stores contactId in scope field, not on the model.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 09:04:04 +07:00
Ruslan Bakiev
693a96cffd feat: auto-create ClientTimelineEntry via Prisma middleware
CalendarEvent and FeedCard now automatically get a ClientTimelineEntry
when created/updated with a contactId. This ensures events created by
the agent (or any other code path) appear in the contact timeline
without needing explicit upsertClientTimelineEntry calls.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 09:01:30 +07:00
Ruslan Bakiev
bf7f4ae933 feat: broadcast pilot agent traces via WebSocket for live status on reconnect
Agent trace logs are now stored in-memory (pilotRunStore) and broadcast
through the existing /ws/crm-updates WebSocket channel. When a client
reconnects, it receives a pilot.catchup with all accumulated logs so the
user sees agent progress even after page reload. Three new WS event
types: pilot.trace, pilot.finished, pilot.catchup.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 08:45:32 +07:00
Ruslan Bakiev
b830f3728c fix: show action label (Показать/Скрыть) instead of status in inbox settings
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 08:28:52 +07:00
Ruslan Bakiev
5ff7dc8d65 chore: update instructions submodule to latest
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 08:21:04 +07:00
Ruslan Bakiev
7d647bef25 fix: disable context picker mode after selecting a scope
When a context scope is picked via the pipette, the picker mode now
turns off automatically since the selection is done and shows as a chip.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 08:18:45 +07:00
Ruslan Bakiev
9b6e8291fe fix: waveform not rendering on voice messages after thread switch
The call wave sync watcher fired before DOM was updated (flush: 'pre'),
so ref callbacks hadn't registered host elements yet. Changed to
flush: 'post' so WaveSurfer instances are created after DOM refs exist.
Also fixed getCallItem to read from clientTimelineItems (source of truth)
instead of visibleThreadItems which could be stale.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 08:17:17 +07:00
Ruslan Bakiev
6e3763a5fd fix: refetch contacts after hiding inbox, redirect to most recent chat
After hiding a contact inbox, the contacts list now refetches immediately
so the hidden contact disappears reactively. When the current contact is
removed from the list, the selection jumps to the most recently active
contact (by lastContactAt) instead of the first item in the current sort.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 07:56:10 +07:00
Ruslan Bakiev
292d587fe1 debug(calendar): add console.warn to GSAP zoom animation functions
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 07:32:33 +07:00
Ruslan Bakiev
1a6840cdc6 fix: optimistic message send — no full timeline reload
Instead of calling openCommunicationThread() after sending (which triggered
a full timeline refetch, destroyed audio waveforms, and caused the chat to
jump), we now:
- Optimistically append the sent message to clientTimelineItems
- Scroll to bottom smoothly
- Refresh contacts sidebar for lastMessageText preview
- Auto-scroll only fires on thread switch (empty→loaded), not on every
  timeline update, preserving audio waveform DOM elements

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 07:14:56 +07:00
Ruslan Bakiev
898f0dc0c5 feat: auto-scroll chat to bottom on thread switch and new messages
Add ref to comm-thread-surface container and watch clientTimelineItems
to scroll to bottom via nextTick whenever messages load or update.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 07:07:05 +07:00
Ruslan Bakiev
cb685446a5 fix: show loading spinner when switching between contact threads
Clear old timeline items immediately on thread switch and display a centered
loader until the new conversation loads, instead of showing stale messages.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 21:45:01 +07:00
Ruslan Bakiev
3ff9120070 fix: export isCommCallPlaying from useCallAudio composable
Same issue as isCommCallPlayable — used in template but not exported/destructured.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 21:09:39 +07:00
Ruslan Bakiev
c07ef2026d fix: export isCommCallPlayable from useCallAudio composable
Function was used in CrmWorkspaceApp template but not exported/destructured,
causing TypeError at runtime.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 21:00:10 +07:00
Ruslan Bakiev
5492e0d05c feat: unread message tracking with blue dot indicator
Add ContactThreadRead model to track when users last viewed each contact thread.
Contacts with messages newer than the last read time show a blue dot in the sidebar.
Opening a thread automatically marks it as read via markThreadRead mutation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 20:25:32 +07:00
Ruslan Bakiev
643d8d02ba feat: granular WebSocket message.new events
- WebSocket now detects new ContactMessages and broadcasts
  message.new events with contactId, text, channel, direction
- Frontend handles message.new: refreshes timeline for open chat,
  refreshes contacts for sidebar preview update
- dashboard.changed still fires for non-message changes

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 20:04:55 +07:00
Ruslan Bakiev
ac9c50b47d feat: remove CommunicationsQuery, load messages on-demand only
- Remove bulk CommunicationsQuery from useContacts (was loading ALL
  messages for ALL contacts on init)
- Rebuild commThreads from contacts + contactInboxes using the new
  lastMessageText field from Phase 1
- Per-contact messages now load on-demand via getClientTimeline
- Remove commItems from useWorkspaceRouting, use clientTimelineItems

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 20:02:58 +07:00
Ruslan Bakiev
601de37ab0 feat: add lastMessageText and lastMessageChannel to contacts query
Enriches the contacts resolver to include the last message preview
and channel, so the sidebar can show thread previews without loading
all communications. No frontend changes yet — fields returned but unused.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 16:02:02 +07:00
Ruslan Bakiev
c229bdee23 fix(calendar): restore GSAP fly-rect + fly-label animation in useCalendar composable
The refactoring in a4d8d81 moved calendar logic into useCalendar.ts but
used the old CSS-transform animation code instead of the GSAP-based
flying rect + flying label implementation. This restores:

- GSAP-based animateCalendarZoomIntoSource and animateCalendarFlipTransition
- Flying label that animates from card title → toolbar on zoom-in and back
- Clone-and-swap pattern with skeleton content in fly-rect (no text)
- Fly-rect/fly-label refs and setters now live in the composable
- isoWeekNumber() and weekNumber field on monthRows
- Sibling card titles and week numbers faded during zoom
- Removed old CSS-transform camera state and calendarSceneTransformStyle

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 15:50:35 +07:00
Ruslan Bakiev
3775d881f9 fix: pass selectedCommThreadId to refreshSelectedClientTimeline
The function was called without arguments in two places, causing
contactId to be empty string and clearing clientTimelineItems to [].
Messages disappeared from the chat panel as a result.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 15:45:19 +07:00
Ruslan Bakiev
195df8e16a fix: stop aggressive 2s chat polling, use WebSocket instead
- Add refetchChatMessages + refetchChatConversations to
  refetchAllCrmQueries so WebSocket dashboard.changed events
  cover pilot chat updates
- Reduce background polling from 2s to 30s (fallback only)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 15:41:58 +07:00
Ruslan Bakiev
19d001815c fix: add missing ClientTimelineItem import in useDocuments
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 15:33:22 +07:00
Ruslan Bakiev
d892d0c604 refactor: distribute types from crm-types.ts to owning composables
Each composable now owns its types and exports them. Other composables
import types from the owning composable. Deleted centralized crm-types.ts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 15:21:30 +07:00
Ruslan Bakiev
a4d8d81de9 refactor: decompose CrmWorkspaceApp.vue into 15 composables
Split the 6000+ line monolithic component into modular composables:
- crm-types.ts: shared types and utility functions
- useAuth, useContacts, useContactInboxes, useCalendar, useDeals,
  useDocuments, useFeed, useTimeline, usePilotChat, useCallAudio,
  usePins, useChangeReview, useCrmRealtime, useWorkspaceRouting
CrmWorkspaceApp.vue is now a thin orchestrator (~2500 lines) that
wires composables together with glue code, keeping template and
styles intact.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 15:05:01 +07:00
Ruslan Bakiev
e5ad3809e0 feat(calendar): flying label animation from card title to toolbar on zoom
The label (month name, week number, day label) now animates from its
position above the source card to the toolbar center on zoom-in, and
flies back from toolbar to the target card title on zoom-out. The
fly-rect rectangle no longer contains text — only skeleton placeholder
lines. Sibling card titles and week numbers also fade during zoom.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 15:03:16 +07:00
Ruslan Bakiev
00e036946c feat(calendar): move labels outside card borders for visual continuity
- Month names moved above month cards in year view (outside article border)
  Hidden in non-year views; toolbar shows context label instead
- Day labels (Mon 24, Tue 25) moved above day columns in week view
- Each card/column wrapped in flex container: title above, card below
- New .calendar-card-title CSS: 11px, 55% opacity, subtle label above card
- No duplicate headers: toolbar is the single source of current context

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 14:42:09 +07:00
Ruslan Bakiev
9505cecab2 feat(calendar): header continuity with week numbers + skeleton content in fly-rect
- Add ISO week numbers to the left of week rows in month view (8, 9, 10...)
  with spacer alignment on day-of-week headers
- Inject label + skeleton placeholder lines into fly-rect during zoom animations:
  zoom-in shows source label (month name / "Week N" / day name) + pulsing bars
  zoom-out shows target context label + skeleton
- Skeleton CSS uses pulse animation (0.8s alternate) for loading hint
- Non-scoped style block for dynamically injected innerHTML elements
- isoWeekNumber helper for ISO 8601 week calculation
- Extended MonthRow type with weekNumber property

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 14:28:31 +07:00
Ruslan Bakiev
77141978c5 feat(calendar): seamless zoom animation with clone-and-swap + full-area coverage
Zoom-in: fade siblings → fade source content → clone source style to fly-rect →
hide source → animate fly-rect to viewport → switch view → fade in new content.

Zoom-out: fade scene → show fly-rect at viewport → switch view → clone target
style → animate fly-rect to target → fade in scene.

Full-area: all views (month/week/day) now fill 100% of container height.
Month grid rows stretch equally, day cells fill row height, depth layers flex.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 11:56:42 +07:00
Ruslan Bakiev
227030b9ae feat(calendar): replace CSS-transform zoom with GSAP flying-rect animation and scope data to year
- Add CalendarDateRange input to GraphQL schema; server resolver now accepts from/to params
- Frontend query sends year-scoped date range variables reactively
- Rewrite zoom-in/zoom-out animations using GSAP flying-rect overlay (650ms vs 2400ms)
- Add flying-rect element to CrmCalendarPanel with proper CSS
- Remove old calendarSceneTransformStyle CSS-transition approach
- Add calendarKillTweens cleanup in onBeforeUnmount

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 11:41:35 +07:00
Ruslan Bakiev
638652b4d8 fix(calendar-lab): enable hover on grid cells by removing pointer-events block
Content overlay layer was intercepting mouse events (pointer-events: auto
with z-index: 5), preventing :hover from reaching the grid cells underneath.
Removed pointer-events: auto from .canvas-content-visible since the content
layer is purely visual and needs no click handling.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 11:16:20 +07:00
Ruslan Bakiev
f553c26931 fix: add browserHttpEndpoint for client-side Apollo requests
The Apollo Client was using httpEndpoint (http://localhost:3000/api/graphql)
for browser requests, which fails on remote servers. Added browserHttpEndpoint
as relative URL "/api/graphql" so browser requests go to the correct origin.
Also added error logging to setInboxHidden mutation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 10:40:08 +07:00
Ruslan Bakiev
5657da13c1 feat(calendar-lab): add hover-targeted zoom with progressive tension and zoom slider
- Zoom animation now targets the hovered cell (not a fixed demo block)
- Progressive "pull" tension: cell scales 5%→10% over 2 scroll ticks
  before the full flying-rect animation triggers (400ms decay timeout)
- Added zoom slider in top-right toolbar matching production design
  (range 0-3 with dot markers, drives step-by-step zoom animations)
- Slider handles mid-drag target updates via sliderTarget variable

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 10:36:22 +07:00
Ruslan Bakiev
947ef4d56d refactor: migrate CRM data layer from manual gqlFetch to Apollo Client
Replace custom gqlFetch() with proper Apollo useQuery/useMutation hooks
powered by codegen-generated TypedDocumentNode types. Key changes:

- Add GraphQL SDL schema file and codegen config for typescript-vue-apollo
- Replace all 28 raw .graphql imports with generated typed documents
- Add 12 useQuery() hooks with cache-and-network fetch policy
- Add 17 useMutation() hooks with surgical refetchQueries per mutation
- Optimistic cache update for setContactInboxHidden (instant archive UX)
- Fix contact list subtitle: show lastText instead of channel name
- Migrate login page from gqlFetch to useMutation
- WebSocket realtime now calls Apollo refetch instead of full data reload

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 10:07:35 +07:00
Ruslan Bakiev
3e711a5533 fix(calendar-lab): rewrite zoom as GSAP flying-rect with proper async sequencing
- Replace panzoom + timeline approach with clean async/await GSAP tweens
- Flying rect morphs from cell position to full viewport (aspect ratio change)
- Fix zoomOut race condition: nested gsap.to inside tl.call fired outside timeline
- Fix opacity conflict: GSAP controls all opacity, CSS class only for pointer-events
- Fix gridLayerRef losing reference on :key remount during resize
- Make viewport dimensions reactive via ResizeObserver (vpWidth/vpHeight refs)
- Wait for fade-in completion before unlocking isAnimating

Co-Authored-By: Claude <noreply@anthropic.com>
2026-02-24 09:47:45 +07:00
Ruslan Bakiev
b316b024be feat(calendar-lab): replace tldraw with two-layer panzoom canvas
Drop tldraw/React dependency in favor of @panzoom/panzoom with a
two-layer architecture: outline rectangles (borders only) are zoomed
via CSS transforms while HTML content renders at native 1:1 scale
as a fade-in overlay — eliminating blur at any zoom level.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-02-24 01:09:46 +07:00
Ruslan Bakiev
1db8e58da1 fix(calendar-lab): rewrite tldraw zoom as LOD — render only current level shapes
Previous implementation created all ~589 shapes (year + months + weeks + days)
at once, causing visual chaos on load and broken zoom. New approach dynamically
creates/destroys shapes per level: year shows 12 months, month shows 6 weeks,
week shows 7 days, day shows time slots. Wheel prime pattern (2 ticks) prevents
accidental zooms. Double-click resets to year view.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-02-24 00:52:58 +07:00
Ruslan Bakiev
6cce211c0b fix(calendar-lab): use local tldraw runtime to avoid react/cdn instance mismatch 2026-02-23 19:47:57 +07:00
Ruslan Bakiev
c5d3a90413 refactor(voice): extract chat dictation into reusable component 2026-02-23 19:43:00 +07:00
Ruslan Bakiev
c1e8f912d1 fix(communications): restore voice dictation in message composer 2026-02-23 19:34:39 +07:00
Ruslan Bakiev
ed78532260 feat(calendar-lab): use tldraw canvas engine with nested zoom rectangles 2026-02-23 19:29:52 +07:00
Ruslan Bakiev
94d8d46693 fix telegram contact identity context for in/out messages 2026-02-23 19:28:03 +07:00
Ruslan Bakiev
79f1012f41 fix(calendar-lab): restore nested calendar canvas demo on zoom route 2026-02-23 19:22:14 +07:00
Ruslan Bakiev
faea65dfcb style(communications): show call play button only on waveform hover 2026-02-23 19:21:54 +07:00
Ruslan Bakiev
bb628a7c0d feat(calendar-lab): switch demo route to tldraw canvas preview 2026-02-23 18:48:34 +07:00
Ruslan Bakiev
2e1014d726 style(communications): center play control on call waveform 2026-02-23 18:44:35 +07:00
Ruslan Bakiev
5fb8113ed7 feat(communications): add play/pause for call voice messages 2026-02-23 18:42:21 +07:00
Ruslan Bakiev
2a3d18f326 compact pilot change summary row in sidebar 2026-02-23 18:37:44 +07:00