feat: task time slots, calendar block drag-to-move

- TaskForm: add start_time/end_time fields; on save emits optional
  block payload so PlannerView creates a PlannedBlock automatically
- PlannerView: handleSave accepts block param, calls createBlock after
  task creation when time is provided
- CalendarBlock: planned blocks with task_id get draggable="true" +
  @dragstart emitting blockDragStart event
- CalendarGrid: forward blockDragStart to useCalendarDnD
- useCalendarDnD: onBlockDragStart stores block_id + duration in
  dataTransfer; onDrop handles both move-existing-block and
  create-new-block paths

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Vadym Samoilenko 2026-05-06 21:19:49 +01:00
parent 732e692c8a
commit 433512fc78
42 changed files with 122 additions and 54 deletions

View file

@ -1 +1 @@
import{d as p,u as y,x as h,c as r,a as t,e as n,n as v,w as d,f as b,r as u,o as s,F as g,l as k,t as a,k as m,i as A}from"./index-B9hhyP-T.js";import{a as w}from"./admin-CT_XX4Td.js";import{_ as B,a as S}from"./CardContent.vue_vue_type_script_setup_true_lang-B1uPgvNK.js";import{_ as x}from"./Badge.vue_vue_type_script_setup_true_lang-BV0smx_q.js";import{_ as V,a as $}from"./utils-DuVQys2y.js";const N={class:"p-6"},C={key:0,class:"flex items-center justify-center h-20"},D={class:"w-full"},E={class:"px-4 py-3"},F={class:"text-sm font-medium text-foreground"},R={class:"px-4 py-3 text-sm text-muted-foreground"},U={class:"px-4 py-3"},j={class:"px-4 py-3"},I={class:"px-4 py-3 text-xs text-muted-foreground"},G=p({__name:"AdminView",setup(J){const f=y(),_=b(),i=u([]),l=u(!1);return h(async()=>{if(!f.isAdmin){_.push("/");return}l.value=!0;try{const c=await w.users();i.value=c.data}finally{l.value=!1}}),(c,o)=>(s(),r("div",N,[o[1]||(o[1]=t("h2",{class:"text-lg font-semibold text-foreground mb-6"},"Admin — Users",-1)),l.value?(s(),r("div",C,[n(V,{class:"text-primary"})])):(s(),v(B,{key:1},{default:d(()=>[n(S,{class:"p-0"},{default:d(()=>[t("table",D,[o[0]||(o[0]=t("thead",null,[t("tr",{class:"border-b border-border"},[t("th",{class:"text-left text-xs font-medium text-muted-foreground px-4 py-3"},"User"),t("th",{class:"text-left text-xs font-medium text-muted-foreground px-4 py-3"},"Email"),t("th",{class:"text-left text-xs font-medium text-muted-foreground px-4 py-3"},"Role"),t("th",{class:"text-left text-xs font-medium text-muted-foreground px-4 py-3"},"Status"),t("th",{class:"text-left text-xs font-medium text-muted-foreground px-4 py-3"},"Joined")])],-1)),t("tbody",null,[(s(!0),r(g,null,k(i.value,e=>(s(),r("tr",{key:e.id,class:"border-b border-border last:border-0 hover:bg-muted/30"},[t("td",E,[t("p",F,a(e.username),1)]),t("td",R,a(e.email),1),t("td",U,[n(x,{variant:e.role==="admin"?"default":"secondary",class:"text-xs"},{default:d(()=>[m(a(e.role),1)]),_:2},1032,["variant"])]),t("td",j,[n(x,{variant:e.is_active?"success":"outline",class:"text-xs"},{default:d(()=>[m(a(e.is_active?"Active":"Inactive"),1)]),_:2},1032,["variant"])]),t("td",I,a(A($)(e.created_at)),1)]))),128))])])]),_:1})]),_:1}))]))}});export{G as default};
import{d as p,u as y,x as h,c as r,a as t,e as n,n as v,w as d,f as b,r as u,o as s,F as g,l as k,t as a,k as m,i as A}from"./index-DBJT1cIA.js";import{a as w}from"./admin-BK3S_0nn.js";import{_ as B,a as S}from"./CardContent.vue_vue_type_script_setup_true_lang-C1186bj-.js";import{_ as x}from"./Badge.vue_vue_type_script_setup_true_lang-DWhAdLQt.js";import{_ as V,a as $}from"./utils-1boGOziL.js";const N={class:"p-6"},C={key:0,class:"flex items-center justify-center h-20"},D={class:"w-full"},E={class:"px-4 py-3"},F={class:"text-sm font-medium text-foreground"},R={class:"px-4 py-3 text-sm text-muted-foreground"},U={class:"px-4 py-3"},j={class:"px-4 py-3"},I={class:"px-4 py-3 text-xs text-muted-foreground"},G=p({__name:"AdminView",setup(J){const f=y(),_=b(),i=u([]),l=u(!1);return h(async()=>{if(!f.isAdmin){_.push("/");return}l.value=!0;try{const c=await w.users();i.value=c.data}finally{l.value=!1}}),(c,o)=>(s(),r("div",N,[o[1]||(o[1]=t("h2",{class:"text-lg font-semibold text-foreground mb-6"},"Admin — Users",-1)),l.value?(s(),r("div",C,[n(V,{class:"text-primary"})])):(s(),v(B,{key:1},{default:d(()=>[n(S,{class:"p-0"},{default:d(()=>[t("table",D,[o[0]||(o[0]=t("thead",null,[t("tr",{class:"border-b border-border"},[t("th",{class:"text-left text-xs font-medium text-muted-foreground px-4 py-3"},"User"),t("th",{class:"text-left text-xs font-medium text-muted-foreground px-4 py-3"},"Email"),t("th",{class:"text-left text-xs font-medium text-muted-foreground px-4 py-3"},"Role"),t("th",{class:"text-left text-xs font-medium text-muted-foreground px-4 py-3"},"Status"),t("th",{class:"text-left text-xs font-medium text-muted-foreground px-4 py-3"},"Joined")])],-1)),t("tbody",null,[(s(!0),r(g,null,k(i.value,e=>(s(),r("tr",{key:e.id,class:"border-b border-border last:border-0 hover:bg-muted/30"},[t("td",E,[t("p",F,a(e.username),1)]),t("td",R,a(e.email),1),t("td",U,[n(x,{variant:e.role==="admin"?"default":"secondary",class:"text-xs"},{default:d(()=>[m(a(e.role),1)]),_:2},1032,["variant"])]),t("td",j,[n(x,{variant:e.is_active?"success":"outline",class:"text-xs"},{default:d(()=>[m(a(e.is_active?"Active":"Inactive"),1)]),_:2},1032,["variant"])]),t("td",I,a(A($)(e.created_at)),1)]))),128))])])]),_:1})]),_:1}))]))}});export{G as default};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1 +1 @@
import{c as a}from"./utils-DuVQys2y.js";import{d as n,o as s,c as o,p as d,i,s as c}from"./index-B9hhyP-T.js";const f=n({__name:"Badge",props:{variant:{default:"default"},class:{}},setup(r){const e=r;return(t,l)=>(s(),o("span",{class:d(i(a)("inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-semibold transition-colors",{"bg-primary text-primary-foreground":e.variant==="default","bg-secondary text-secondary-foreground":e.variant==="secondary","bg-destructive text-destructive-foreground":e.variant==="destructive","border border-border text-foreground":e.variant==="outline","bg-emerald-500/20 text-emerald-400":e.variant==="success","bg-amber-500/20 text-amber-400":e.variant==="warning"},e.class))},[c(t.$slots,"default")],2))}});export{f as _};
import{c as a}from"./utils-1boGOziL.js";import{d as n,o as s,c as o,p as d,i,s as c}from"./index-DBJT1cIA.js";const f=n({__name:"Badge",props:{variant:{default:"default"},class:{}},setup(r){const e=r;return(t,l)=>(s(),o("span",{class:d(i(a)("inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-semibold transition-colors",{"bg-primary text-primary-foreground":e.variant==="default","bg-secondary text-secondary-foreground":e.variant==="secondary","bg-destructive text-destructive-foreground":e.variant==="destructive","border border-border text-foreground":e.variant==="outline","bg-emerald-500/20 text-emerald-400":e.variant==="success","bg-amber-500/20 text-amber-400":e.variant==="warning"},e.class))},[c(t.$slots,"default")],2))}});export{f as _};

View file

@ -1 +1 @@
import{c,_ as l}from"./utils-DuVQys2y.js";import{d as u,c as f,p as m,n as b,j as v,s as g,m as p,o as n}from"./index-B9hhyP-T.js";const y=["type","disabled"],k=u({__name:"Button",props:{variant:{default:"default"},size:{default:"md"},loading:{type:Boolean,default:!1},disabled:{type:Boolean,default:!1},type:{default:"button"},class:{}},emits:["click"],setup(t,{emit:o}){const e=t,a=o,r=p(()=>c("inline-flex items-center justify-center rounded-md font-medium transition-colors","focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2","disabled:pointer-events-none disabled:opacity-50",{"bg-primary text-primary-foreground hover:bg-primary/90":e.variant==="default","border border-input bg-background hover:bg-accent hover:text-accent-foreground":e.variant==="outline","hover:bg-accent hover:text-accent-foreground":e.variant==="ghost","bg-destructive text-destructive-foreground hover:bg-destructive/90":e.variant==="destructive","bg-secondary text-secondary-foreground hover:bg-secondary/80":e.variant==="secondary","underline-offset-4 hover:underline text-primary":e.variant==="link","h-8 px-3 text-xs":e.size==="sm","h-10 px-4 py-2 text-sm":e.size==="md","h-11 px-8 text-base":e.size==="lg","h-9 w-9 p-0":e.size==="icon"},e.class));return(i,s)=>(n(),f("button",{class:m(r.value),type:t.type,disabled:t.disabled||t.loading,onClick:s[0]||(s[0]=d=>a("click",d))},[t.loading?(n(),b(l,{key:0,size:"sm",class:"mr-2"})):v("",!0),g(i.$slots,"default")],10,y))}});export{k as _};
import{c,_ as l}from"./utils-1boGOziL.js";import{d as u,c as f,p as m,n as b,j as v,s as g,m as p,o as n}from"./index-DBJT1cIA.js";const y=["type","disabled"],k=u({__name:"Button",props:{variant:{default:"default"},size:{default:"md"},loading:{type:Boolean,default:!1},disabled:{type:Boolean,default:!1},type:{default:"button"},class:{}},emits:["click"],setup(t,{emit:o}){const e=t,a=o,r=p(()=>c("inline-flex items-center justify-center rounded-md font-medium transition-colors","focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2","disabled:pointer-events-none disabled:opacity-50",{"bg-primary text-primary-foreground hover:bg-primary/90":e.variant==="default","border border-input bg-background hover:bg-accent hover:text-accent-foreground":e.variant==="outline","hover:bg-accent hover:text-accent-foreground":e.variant==="ghost","bg-destructive text-destructive-foreground hover:bg-destructive/90":e.variant==="destructive","bg-secondary text-secondary-foreground hover:bg-secondary/80":e.variant==="secondary","underline-offset-4 hover:underline text-primary":e.variant==="link","h-8 px-3 text-xs":e.size==="sm","h-10 px-4 py-2 text-sm":e.size==="md","h-11 px-8 text-base":e.size==="lg","h-9 w-9 p-0":e.size==="icon"},e.class));return(i,s)=>(n(),f("button",{class:m(r.value),type:t.type,disabled:t.disabled||t.loading,onClick:s[0]||(s[0]=d=>a("click",d))},[t.loading?(n(),b(l,{key:0,size:"sm",class:"mr-2"})):v("",!0),g(i.$slots,"default")],10,y))}});export{k as _};

File diff suppressed because one or more lines are too long

View file

@ -1 +1 @@
.calendar-block--manual[data-v-5829880c]{background-image:repeating-linear-gradient(45deg,transparent,transparent 3px,rgba(255,255,255,.1) 3px,rgba(255,255,255,.1) 6px)}
.calendar-block--manual[data-v-978cfc69]{background-image:repeating-linear-gradient(45deg,transparent,transparent 3px,rgba(255,255,255,.1) 3px,rgba(255,255,255,.1) 6px)}

File diff suppressed because one or more lines are too long

View file

@ -1 +1 @@
import{c as e}from"./utils-DuVQys2y.js";import{d as o,c as t,p as n,i as c,s as p,o as l}from"./index-B9hhyP-T.js";const _=o({__name:"Card",props:{class:{}},setup(s){const a=s;return(r,d)=>(l(),t("div",{class:n(c(e)("rounded-lg border bg-card text-card-foreground shadow-sm",a.class))},[p(r.$slots,"default")],2))}}),f=o({__name:"CardContent",props:{class:{}},setup(s){const a=s;return(r,d)=>(l(),t("div",{class:n(c(e)("p-6 pt-0",a.class))},[p(r.$slots,"default")],2))}});export{_,f as a};
import{c as e}from"./utils-1boGOziL.js";import{d as o,c as t,p as n,i as c,s as p,o as l}from"./index-DBJT1cIA.js";const _=o({__name:"Card",props:{class:{}},setup(s){const a=s;return(r,d)=>(l(),t("div",{class:n(c(e)("rounded-lg border bg-card text-card-foreground shadow-sm",a.class))},[p(r.$slots,"default")],2))}}),f=o({__name:"CardContent",props:{class:{}},setup(s){const a=s;return(r,d)=>(l(),t("div",{class:n(c(e)("p-6 pt-0",a.class))},[p(r.$slots,"default")],2))}});export{_,f as a};

View file

@ -1 +1 @@
import{c as t}from"./utils-DuVQys2y.js";import{d as o,o as n,c as r,p as c,i as l,s as p}from"./index-B9hhyP-T.js";const f=o({__name:"CardHeader",props:{class:{}},setup(s){const e=s;return(a,i)=>(n(),r("div",{class:c(l(t)("flex flex-col space-y-1.5 p-6",e.class))},[p(a.$slots,"default")],2))}}),_=o({__name:"CardTitle",props:{class:{}},setup(s){const e=s;return(a,i)=>(n(),r("h3",{class:c(l(t)("text-lg font-semibold leading-none tracking-tight",e.class))},[p(a.$slots,"default")],2))}});export{f as _,_ as a};
import{c as t}from"./utils-1boGOziL.js";import{d as o,o as n,c as r,p as c,i as l,s as p}from"./index-DBJT1cIA.js";const f=o({__name:"CardHeader",props:{class:{}},setup(s){const e=s;return(a,i)=>(n(),r("div",{class:c(l(t)("flex flex-col space-y-1.5 p-6",e.class))},[p(a.$slots,"default")],2))}}),_=o({__name:"CardTitle",props:{class:{}},setup(s){const e=s;return(a,i)=>(n(),r("h3",{class:c(l(t)("text-lg font-semibold leading-none tracking-tight",e.class))},[p(a.$slots,"default")],2))}});export{f as _,_ as a};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1 @@
import{d as S,x as $,c as a,a as s,i as r,n as x,w as l,j as u,e as d,o as n,k as m,t as i,F as _,l as p,p as y,f as I,r as z,m as A,K as v}from"./index-DBJT1cIA.js";import{u as j}from"./devops-oP6yqP0k.js";import{_ as k,a as h}from"./CardContent.vue_vue_type_script_setup_true_lang-C1186bj-.js";import{a as D,_ as V}from"./CardTitle.vue_vue_type_script_setup_true_lang-y4H1mp2-.js";import{_ as B}from"./Button.vue_vue_type_script_setup_true_lang-CBskCLXi.js";import{_ as b}from"./utils-1boGOziL.js";const L={class:"p-6 space-y-6"},W={class:"flex items-center justify-between gap-4 flex-wrap"},F={class:"flex items-center gap-2"},R={key:0,class:"flex items-center gap-2 text-sm text-muted-foreground"},E={key:1,class:"flex items-center gap-3"},K={class:"text-sm text-foreground"},O={key:0,class:"text-xs text-muted-foreground ml-2"},G={key:2,class:"flex items-center gap-3"},M={key:3,class:"text-xs text-destructive mt-2"},T={class:"flex items-center justify-between gap-3 flex-wrap"},q={class:"flex items-center rounded-lg border border-border overflow-hidden bg-muted/30"},H=["onClick"],J={key:0,class:"flex items-center justify-center py-8"},P={key:1,class:"text-center py-8 text-sm text-muted-foreground"},Q={key:2,class:"space-y-1"},U={class:"text-xs font-mono text-muted-foreground w-10 shrink-0"},X={class:"flex-1 min-w-0"},Y={class:"text-sm text-foreground truncate"},Z={class:"text-xs text-muted-foreground"},tt=["href"],lt=S({__name:"DevopsView",setup(et){const w=I(),t=j(),c=z("All");$(async()=>{await t.fetchIntegration(),t.integration&&await t.fetchWorkItems()});const f=A(()=>c.value==="All"?t.workItems:t.workItems.filter(g=>g.state===c.value));async function C(){try{await t.sync(),v.success("Sync complete"),await t.fetchWorkItems()}catch{v.error(t.error??"Sync failed")}}return(g,e)=>(n(),a("div",L,[s("div",W,[e[2]||(e[2]=s("h2",{class:"text-lg font-semibold text-foreground"},"Azure DevOps",-1)),s("div",F,[r(t).integration?(n(),x(B,{key:0,variant:"outline",size:"sm",loading:r(t).syncing,onClick:C},{default:l(()=>[...e[1]||(e[1]=[m(" Sync Now ",-1)])]),_:1},8,["loading"])):u("",!0)])]),d(k,null,{default:l(()=>[d(h,{class:"pt-4"},{default:l(()=>{var o;return[r(t).loading&&!r(t).integration?(n(),a("div",R,[d(b,{size:"sm"}),e[3]||(e[3]=s("span",null,"Loading...",-1))])):r(t).integration?(n(),a("div",E,[e[6]||(e[6]=s("div",{class:"h-2 w-2 rounded-full bg-[hsl(var(--success))]"},null,-1)),s("span",K,[e[4]||(e[4]=m(" Connected to ",-1)),s("strong",null,i(r(t).integration.organization),1),e[5]||(e[5]=m(" / ",-1)),s("strong",null,i(r(t).integration.project),1)]),r(t).integration.last_synced_at?(n(),a("span",O," Last synced: "+i(new Date(r(t).integration.last_synced_at).toLocaleString()),1)):u("",!0)])):(n(),a("div",G,[e[7]||(e[7]=s("div",{class:"h-2 w-2 rounded-full bg-muted-foreground"},null,-1)),e[8]||(e[8]=s("span",{class:"text-sm text-muted-foreground"},"Not connected.",-1)),s("button",{class:"text-sm text-primary hover:underline",onClick:e[0]||(e[0]=N=>r(w).push("/settings"))}," Go to Settings to connect ")])),(o=r(t).integration)!=null&&o.last_sync_error?(n(),a("p",M," Error: "+i(r(t).integration.last_sync_error),1)):u("",!0)]}),_:1})]),_:1}),r(t).integration?(n(),x(k,{key:0},{default:l(()=>[d(V,{class:"pb-2"},{default:l(()=>[s("div",T,[d(D,{class:"text-sm"},{default:l(()=>[...e[9]||(e[9]=[m("Work Items",-1)])]),_:1}),s("div",q,[(n(),a(_,null,p(["All","Active","Resolved","Closed"],o=>s("button",{key:o,class:y(["px-3 py-1 text-xs font-medium transition-colors",c.value===o?"bg-primary text-primary-foreground":"text-muted-foreground hover:text-foreground hover:bg-muted/50"]),onClick:N=>c.value=o},i(o),11,H)),64))])])]),_:1}),d(h,null,{default:l(()=>[r(t).loading?(n(),a("div",J,[d(b,{size:"md",class:"text-primary"})])):f.value.length===0?(n(),a("div",P," No work items found ")):(n(),a("div",Q,[(n(!0),a(_,null,p(f.value,o=>(n(),a("div",{key:o.id,class:"flex items-center gap-3 px-3 py-2.5 rounded-lg hover:bg-muted/30 transition-colors"},[s("span",U,"#"+i(o.ado_id),1),s("div",X,[s("p",Y,i(o.title),1),s("p",Z,i(o.type),1)]),s("span",{class:y(["text-xs px-2 py-0.5 rounded-full shrink-0",o.state==="Active"?"bg-blue-500/10 text-blue-400":o.state==="Resolved"?"bg-green-500/10 text-green-400":(o.state==="Closed","bg-muted text-muted-foreground")])},i(o.state),3),o.url?(n(),a("a",{key:0,href:o.url,target:"_blank",class:"text-xs text-primary hover:underline shrink-0"}," Open → ",8,tt)):u("",!0)]))),128))]))]),_:1})]),_:1})):u("",!0)]))}});export{lt as default};

View file

@ -1 +1 @@
import{d as y,x as k,E as b,n as h,G as x,e as c,T as g,w as u,o as a,c as n,a as o,s as r,t as m,j as i,p as w}from"./index-B9hhyP-T.js";import{_ as $}from"./Button.vue_vue_type_script_setup_true_lang-mCZU1D3b.js";const C={key:0,class:"fixed inset-0 z-50 flex items-center justify-center p-4"},B=["aria-label"],j={key:0,class:"flex items-center justify-between p-6 pb-4"},E={class:"text-lg font-semibold text-foreground"},z={key:0,class:"text-sm text-muted-foreground mt-1"},L={class:"px-6 pb-4"},M={key:1,class:"flex justify-end gap-2 px-6 pb-6"},V=y({__name:"Dialog",props:{open:{type:Boolean},title:{},description:{},maxWidth:{default:"max-w-lg"}},emits:["close"],setup(e,{emit:f}){const p=e,l=f;function d(t){t.key==="Escape"&&p.open&&l("close")}return k(()=>document.addEventListener("keydown",d)),b(()=>document.removeEventListener("keydown",d)),(t,s)=>(a(),h(x,{to:"body"},[c(g,{"enter-active-class":"transition-opacity duration-200","enter-from-class":"opacity-0","enter-to-class":"opacity-100","leave-active-class":"transition-opacity duration-200","leave-from-class":"opacity-100","leave-to-class":"opacity-0"},{default:u(()=>[e.open?(a(),n("div",C,[o("div",{class:"absolute inset-0 bg-black/60 backdrop-blur-sm",onClick:s[0]||(s[0]=v=>l("close"))}),o("div",{class:w(["relative w-full bg-card border border-border rounded-lg shadow-xl z-10",e.maxWidth]),role:"dialog","aria-modal":!0,"aria-label":e.title},[e.title||t.$slots.header?(a(),n("div",j,[o("div",null,[r(t.$slots,"header",{},()=>[o("h2",E,m(e.title),1),e.description?(a(),n("p",z,m(e.description),1)):i("",!0)])]),c($,{variant:"ghost",size:"icon",class:"shrink-0",onClick:s[1]||(s[1]=v=>l("close"))},{default:u(()=>[...s[2]||(s[2]=[o("svg",{class:"h-4 w-4",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},[o("path",{"stroke-linecap":"round","stroke-linejoin":"round","stroke-width":"2",d:"M6 18L18 6M6 6l12 12"})],-1)])]),_:1})])):i("",!0),o("div",L,[r(t.$slots,"default")]),t.$slots.footer?(a(),n("div",M,[r(t.$slots,"footer")])):i("",!0)],10,B)])):i("",!0)]),_:3})]))}});export{V as _};
import{d as y,x as k,E as b,n as h,G as x,e as c,T as g,w as u,o as a,c as n,a as o,s as r,t as m,j as i,p as w}from"./index-DBJT1cIA.js";import{_ as $}from"./Button.vue_vue_type_script_setup_true_lang-CBskCLXi.js";const C={key:0,class:"fixed inset-0 z-50 flex items-center justify-center p-4"},B=["aria-label"],j={key:0,class:"flex items-center justify-between p-6 pb-4"},E={class:"text-lg font-semibold text-foreground"},z={key:0,class:"text-sm text-muted-foreground mt-1"},L={class:"px-6 pb-4"},M={key:1,class:"flex justify-end gap-2 px-6 pb-6"},V=y({__name:"Dialog",props:{open:{type:Boolean},title:{},description:{},maxWidth:{default:"max-w-lg"}},emits:["close"],setup(e,{emit:f}){const p=e,l=f;function d(t){t.key==="Escape"&&p.open&&l("close")}return k(()=>document.addEventListener("keydown",d)),b(()=>document.removeEventListener("keydown",d)),(t,s)=>(a(),h(x,{to:"body"},[c(g,{"enter-active-class":"transition-opacity duration-200","enter-from-class":"opacity-0","enter-to-class":"opacity-100","leave-active-class":"transition-opacity duration-200","leave-from-class":"opacity-100","leave-to-class":"opacity-0"},{default:u(()=>[e.open?(a(),n("div",C,[o("div",{class:"absolute inset-0 bg-black/60 backdrop-blur-sm",onClick:s[0]||(s[0]=v=>l("close"))}),o("div",{class:w(["relative w-full bg-card border border-border rounded-lg shadow-xl z-10",e.maxWidth]),role:"dialog","aria-modal":!0,"aria-label":e.title},[e.title||t.$slots.header?(a(),n("div",j,[o("div",null,[r(t.$slots,"header",{},()=>[o("h2",E,m(e.title),1),e.description?(a(),n("p",z,m(e.description),1)):i("",!0)])]),c($,{variant:"ghost",size:"icon",class:"shrink-0",onClick:s[1]||(s[1]=v=>l("close"))},{default:u(()=>[...s[2]||(s[2]=[o("svg",{class:"h-4 w-4",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},[o("path",{"stroke-linecap":"round","stroke-linejoin":"round","stroke-width":"2",d:"M6 18L18 6M6 6l12 12"})],-1)])]),_:1})])):i("",!0),o("div",L,[r(t.$slots,"default")]),t.$slots.footer?(a(),n("div",M,[r(t.$slots,"footer")])):i("",!0)],10,B)])):i("",!0)]),_:3})]))}});export{V as _};

View file

@ -1 +1 @@
import{c as i}from"./utils-DuVQys2y.js";import{d,c as s,p as u,i as m,o as r}from"./index-B9hhyP-T.js";const c=["id","name","type","value","placeholder","disabled","autocomplete","min","max","step"],g=d({__name:"Input",props:{modelValue:{},type:{},placeholder:{},disabled:{type:Boolean},class:{},id:{},name:{},autocomplete:{},min:{},max:{},step:{}},emits:["update:modelValue","change","blur","focus"],setup(e,{emit:a}){const n=e,o=a;return(f,t)=>(r(),s("input",{id:e.id,name:e.name,type:e.type??"text",value:e.modelValue,placeholder:e.placeholder,disabled:e.disabled,autocomplete:e.autocomplete,min:e.min,max:e.max,step:e.step,class:u(m(i)("flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm","ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium","placeholder:text-muted-foreground","focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2","disabled:cursor-not-allowed disabled:opacity-50",n.class)),onInput:t[0]||(t[0]=l=>o("update:modelValue",l.target.value)),onChange:t[1]||(t[1]=l=>o("change",l.target.value)),onBlur:t[2]||(t[2]=l=>o("blur",l)),onFocus:t[3]||(t[3]=l=>o("focus",l))},null,42,c))}});export{g as _};
import{c as i}from"./utils-1boGOziL.js";import{d,c as s,p as u,i as m,o as r}from"./index-DBJT1cIA.js";const c=["id","name","type","value","placeholder","disabled","autocomplete","min","max","step"],g=d({__name:"Input",props:{modelValue:{},type:{},placeholder:{},disabled:{type:Boolean},class:{},id:{},name:{},autocomplete:{},min:{},max:{},step:{}},emits:["update:modelValue","change","blur","focus"],setup(e,{emit:a}){const n=e,o=a;return(f,t)=>(r(),s("input",{id:e.id,name:e.name,type:e.type??"text",value:e.modelValue,placeholder:e.placeholder,disabled:e.disabled,autocomplete:e.autocomplete,min:e.min,max:e.max,step:e.step,class:u(m(i)("flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm","ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium","placeholder:text-muted-foreground","focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2","disabled:cursor-not-allowed disabled:opacity-50",n.class)),onInput:t[0]||(t[0]=l=>o("update:modelValue",l.target.value)),onChange:t[1]||(t[1]=l=>o("change",l.target.value)),onBlur:t[2]||(t[2]=l=>o("blur",l)),onFocus:t[3]||(t[3]=l=>o("focus",l))},null,42,c))}});export{g as _};

View file

@ -1 +1 @@
import{a as b}from"./admin-CT_XX4Td.js";import{_ as K,a as $}from"./CardContent.vue_vue_type_script_setup_true_lang-B1uPgvNK.js";import{_ as v}from"./Button.vue_vue_type_script_setup_true_lang-mCZU1D3b.js";import{_ as V}from"./Dialog.vue_vue_type_script_setup_true_lang-BF-ub_3g.js";import{_ as N}from"./Input.vue_vue_type_script_setup_true_lang-CBtApgd6.js";import{_ as A,a as k}from"./utils-DuVQys2y.js";import{d as B,x as L,c as l,a as t,e as r,w as n,r as i,o as a,k as p,F as P,l as j,t as u,i as h,n as F,j as I,K as y}from"./index-B9hhyP-T.js";const D={class:"p-6"},R={class:"flex items-center justify-between mb-6"},z={key:0,class:"flex items-center justify-center h-20"},M={key:1,class:"text-center text-muted-foreground py-8 text-sm"},T={key:2,class:"w-full"},U={class:"px-4 py-3 text-sm text-foreground"},E={class:"px-4 py-3 text-sm font-mono text-muted-foreground"},H={class:"px-4 py-3 text-xs text-muted-foreground"},S={class:"px-4 py-3 text-xs text-muted-foreground"},q={class:"px-4 py-3 text-right"},G={class:"space-y-4"},J={key:0,class:"rounded-md bg-emerald-500/10 border border-emerald-500/30 p-3"},O={class:"text-xs font-mono text-foreground break-all"},Q={key:1,class:"space-y-1.5"},le=B({__name:"KeysView",setup(W){const f=i([]),_=i(!1),c=i(!1),m=i(""),x=i(!1),d=i(null);L(()=>g());async function g(){_.value=!0;try{const o=await b.keys();f.value=o.data}finally{_.value=!1}}async function w(){if(m.value.trim()){x.value=!0;try{const o=await b.createKey({label:m.value});d.value=o.data.key,y.success("API key created"),await g(),m.value=""}catch{y.error("Failed to create key")}finally{x.value=!1}}}async function C(o){if(confirm(`Revoke key "${o.label}"? This cannot be undone.`))try{await b.revokeKey(o.id),y.success("Key revoked"),f.value=f.value.filter(e=>e.id!==o.id)}catch{y.error("Failed to revoke key")}}return(o,e)=>(a(),l("div",D,[t("div",R,[e[5]||(e[5]=t("h2",{class:"text-lg font-semibold text-foreground"},"API Keys",-1)),r(v,{size:"sm",onClick:e[0]||(e[0]=s=>{c.value=!0,d.value=null})},{default:n(()=>[...e[4]||(e[4]=[t("svg",{class:"h-4 w-4 mr-1.5",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},[t("path",{"stroke-linecap":"round","stroke-linejoin":"round","stroke-width":"2",d:"M12 4v16m8-8H4"})],-1),p(" New Key ",-1)])]),_:1})]),r(K,null,{default:n(()=>[r($,{class:"p-0"},{default:n(()=>[_.value?(a(),l("div",z,[r(A,{class:"text-primary"})])):f.value.length===0?(a(),l("div",M," No API keys ")):(a(),l("table",T,[e[7]||(e[7]=t("thead",null,[t("tr",{class:"border-b border-border"},[t("th",{class:"text-left text-xs font-medium text-muted-foreground px-4 py-3"},"Label"),t("th",{class:"text-left text-xs font-medium text-muted-foreground px-4 py-3"},"Prefix"),t("th",{class:"text-left text-xs font-medium text-muted-foreground px-4 py-3"},"Created"),t("th",{class:"text-left text-xs font-medium text-muted-foreground px-4 py-3"},"Last Used"),t("th",{class:"px-4 py-3"})])],-1)),t("tbody",null,[(a(!0),l(P,null,j(f.value,s=>(a(),l("tr",{key:s.id,class:"border-b border-border last:border-0 hover:bg-muted/30"},[t("td",U,u(s.label),1),t("td",E,u(s.prefix)+"...",1),t("td",H,u(h(k)(s.created_at)),1),t("td",S,u(s.last_used?h(k)(s.last_used):"Never"),1),t("td",q,[r(v,{variant:"ghost",size:"sm",class:"text-destructive",onClick:X=>C(s)},{default:n(()=>[...e[6]||(e[6]=[p(" Revoke ",-1)])]),_:1},8,["onClick"])])]))),128))])]))]),_:1})]),_:1}),r(V,{open:c.value,title:"Create API Key",onClose:e[3]||(e[3]=s=>c.value=!1)},{footer:n(()=>[r(v,{variant:"outline",onClick:e[2]||(e[2]=s=>c.value=!1)},{default:n(()=>[p(u(d.value?"Done":"Cancel"),1)]),_:1}),d.value?I("",!0):(a(),F(v,{key:0,loading:x.value,onClick:w},{default:n(()=>[...e[10]||(e[10]=[p(" Create ",-1)])]),_:1},8,["loading"]))]),default:n(()=>[t("div",G,[d.value?(a(),l("div",J,[e[8]||(e[8]=t("p",{class:"text-xs text-emerald-400 font-medium mb-1"},"Key created — save it now!",-1)),t("p",O,u(d.value),1)])):(a(),l("div",Q,[e[9]||(e[9]=t("label",{class:"text-sm font-medium text-foreground"},"Label",-1)),r(N,{modelValue:m.value,"onUpdate:modelValue":e[1]||(e[1]=s=>m.value=s),placeholder:"e.g. claude-collector",disabled:x.value},null,8,["modelValue","disabled"])]))])]),_:1},8,["open"])]))}});export{le as default};
import{a as b}from"./admin-BK3S_0nn.js";import{_ as K,a as $}from"./CardContent.vue_vue_type_script_setup_true_lang-C1186bj-.js";import{_ as v}from"./Button.vue_vue_type_script_setup_true_lang-CBskCLXi.js";import{_ as V}from"./Dialog.vue_vue_type_script_setup_true_lang-DTE9ZTh_.js";import{_ as N}from"./Input.vue_vue_type_script_setup_true_lang-C-EOU0Fh.js";import{_ as A,a as k}from"./utils-1boGOziL.js";import{d as B,x as L,c as l,a as t,e as r,w as n,r as i,o as a,k as p,F as P,l as j,t as u,i as h,n as F,j as I,K as y}from"./index-DBJT1cIA.js";const D={class:"p-6"},R={class:"flex items-center justify-between mb-6"},z={key:0,class:"flex items-center justify-center h-20"},M={key:1,class:"text-center text-muted-foreground py-8 text-sm"},T={key:2,class:"w-full"},U={class:"px-4 py-3 text-sm text-foreground"},E={class:"px-4 py-3 text-sm font-mono text-muted-foreground"},H={class:"px-4 py-3 text-xs text-muted-foreground"},S={class:"px-4 py-3 text-xs text-muted-foreground"},q={class:"px-4 py-3 text-right"},G={class:"space-y-4"},J={key:0,class:"rounded-md bg-emerald-500/10 border border-emerald-500/30 p-3"},O={class:"text-xs font-mono text-foreground break-all"},Q={key:1,class:"space-y-1.5"},le=B({__name:"KeysView",setup(W){const f=i([]),_=i(!1),c=i(!1),m=i(""),x=i(!1),d=i(null);L(()=>g());async function g(){_.value=!0;try{const o=await b.keys();f.value=o.data}finally{_.value=!1}}async function w(){if(m.value.trim()){x.value=!0;try{const o=await b.createKey({label:m.value});d.value=o.data.key,y.success("API key created"),await g(),m.value=""}catch{y.error("Failed to create key")}finally{x.value=!1}}}async function C(o){if(confirm(`Revoke key "${o.label}"? This cannot be undone.`))try{await b.revokeKey(o.id),y.success("Key revoked"),f.value=f.value.filter(e=>e.id!==o.id)}catch{y.error("Failed to revoke key")}}return(o,e)=>(a(),l("div",D,[t("div",R,[e[5]||(e[5]=t("h2",{class:"text-lg font-semibold text-foreground"},"API Keys",-1)),r(v,{size:"sm",onClick:e[0]||(e[0]=s=>{c.value=!0,d.value=null})},{default:n(()=>[...e[4]||(e[4]=[t("svg",{class:"h-4 w-4 mr-1.5",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},[t("path",{"stroke-linecap":"round","stroke-linejoin":"round","stroke-width":"2",d:"M12 4v16m8-8H4"})],-1),p(" New Key ",-1)])]),_:1})]),r(K,null,{default:n(()=>[r($,{class:"p-0"},{default:n(()=>[_.value?(a(),l("div",z,[r(A,{class:"text-primary"})])):f.value.length===0?(a(),l("div",M," No API keys ")):(a(),l("table",T,[e[7]||(e[7]=t("thead",null,[t("tr",{class:"border-b border-border"},[t("th",{class:"text-left text-xs font-medium text-muted-foreground px-4 py-3"},"Label"),t("th",{class:"text-left text-xs font-medium text-muted-foreground px-4 py-3"},"Prefix"),t("th",{class:"text-left text-xs font-medium text-muted-foreground px-4 py-3"},"Created"),t("th",{class:"text-left text-xs font-medium text-muted-foreground px-4 py-3"},"Last Used"),t("th",{class:"px-4 py-3"})])],-1)),t("tbody",null,[(a(!0),l(P,null,j(f.value,s=>(a(),l("tr",{key:s.id,class:"border-b border-border last:border-0 hover:bg-muted/30"},[t("td",U,u(s.label),1),t("td",E,u(s.prefix)+"...",1),t("td",H,u(h(k)(s.created_at)),1),t("td",S,u(s.last_used?h(k)(s.last_used):"Never"),1),t("td",q,[r(v,{variant:"ghost",size:"sm",class:"text-destructive",onClick:X=>C(s)},{default:n(()=>[...e[6]||(e[6]=[p(" Revoke ",-1)])]),_:1},8,["onClick"])])]))),128))])]))]),_:1})]),_:1}),r(V,{open:c.value,title:"Create API Key",onClose:e[3]||(e[3]=s=>c.value=!1)},{footer:n(()=>[r(v,{variant:"outline",onClick:e[2]||(e[2]=s=>c.value=!1)},{default:n(()=>[p(u(d.value?"Done":"Cancel"),1)]),_:1}),d.value?I("",!0):(a(),F(v,{key:0,loading:x.value,onClick:w},{default:n(()=>[...e[10]||(e[10]=[p(" Create ",-1)])]),_:1},8,["loading"]))]),default:n(()=>[t("div",G,[d.value?(a(),l("div",J,[e[8]||(e[8]=t("p",{class:"text-xs text-emerald-400 font-medium mb-1"},"Key created — save it now!",-1)),t("p",O,u(d.value),1)])):(a(),l("div",Q,[e[9]||(e[9]=t("label",{class:"text-sm font-medium text-foreground"},"Label",-1)),r(N,{modelValue:m.value,"onUpdate:modelValue":e[1]||(e[1]=s=>m.value=s),placeholder:"e.g. claude-collector",disabled:x.value},null,8,["modelValue","disabled"])]))])]),_:1},8,["open"])]))}});export{le as default};

View file

@ -1 +1 @@
import{E as T,r as y,d as J,u as O,x as V,c as f,a as o,p as b,i,t as v,n as $,w as x,j as k,e as C,o as c,k as w,F as B,l as F,m as z}from"./index-B9hhyP-T.js";import{_ as A,a as D}from"./CardContent.vue_vue_type_script_setup_true_lang-B1uPgvNK.js";import{_ as N}from"./Button.vue_vue_type_script_setup_true_lang-mCZU1D3b.js";import"./utils-DuVQys2y.js";function U(E){const e=y([]),l=y(!1),m=y(null);let s=null,r=null,u=!1;function p(){if(!u)try{s=new EventSource(E),s.onopen=()=>{l.value=!0,m.value=null},s.onmessage=n=>{try{const g=JSON.parse(n.data);e.value.push({type:"message",data:g}),e.value.length>200&&e.value.shift()}catch{e.value.push({type:"message",data:n.data})}},s.addEventListener("session_start",n=>{try{e.value.push({type:"session_start",data:JSON.parse(n.data)})}catch{e.value.push({type:"session_start",data:n.data})}e.value.length>200&&e.value.shift()}),s.addEventListener("session_end",n=>{try{e.value.push({type:"session_end",data:JSON.parse(n.data)})}catch{e.value.push({type:"session_end",data:n.data})}e.value.length>200&&e.value.shift()}),s.addEventListener("activity",n=>{try{e.value.push({type:"activity",data:JSON.parse(n.data)})}catch{e.value.push({type:"activity",data:n.data})}e.value.length>200&&e.value.shift()}),s.onerror=()=>{l.value=!1,m.value="Connection lost, reconnecting...",s==null||s.close(),s=null,u||(r=setTimeout(()=>p(),5e3))}}catch{m.value="Failed to connect to event stream",u||(r=setTimeout(()=>p(),5e3))}}function _(){u=!0,r&&clearTimeout(r),s==null||s.close(),s=null,l.value=!1}function h(){e.value=[]}return T(()=>{_()}),{events:e,connected:l,error:m,connect:p,disconnect:_,clearEvents:h}}const I={class:"p-6 h-full flex flex-col"},R={class:"flex items-center gap-3 mb-4"},M={class:"flex items-center gap-2"},P={class:"text-xs text-muted-foreground"},W={key:0,class:"mb-4 text-xs text-amber-400 bg-amber-500/10 border border-amber-500/30 rounded px-3 py-2"},q={key:0,class:"flex items-center justify-center h-full text-sm text-muted-foreground"},G={key:1,class:"overflow-y-auto h-full font-mono text-xs"},H={class:"flex-1 min-w-0"},K={class:"flex items-center gap-2 flex-wrap"},Q={key:0,class:"text-muted-foreground"},X={class:"text-muted-foreground truncate mt-0.5"},se=J({__name:"LiveView",setup(E){const e=O(),l=e.getToken(),m=`/cc-dashboard/api/events${l?`?token=${encodeURIComponent(l)}`:""}`,{events:s,connected:r,error:u,connect:p,clearEvents:_}=U(m);V(()=>{e.isAuthenticated&&l&&p()});const h=z(()=>[...s.value].reverse().slice(0,100));function n(t){return t==="session_start"?"text-emerald-400":t==="session_end"?"text-amber-400":t==="activity"?"text-blue-400":"text-muted-foreground"}function g(t){return t==="session_start"?"▶":t==="session_end"?"■":t==="activity"?"●":"○"}function j(t){if(typeof t=="string")return t;if(t&&typeof t=="object"){const a=t;return a.message||a.summary||JSON.stringify(t)}return String(t)}function S(t){if(t&&typeof t=="object"){const a=t;return a.display_name||a.project_id||""}return""}return(t,a)=>(c(),f("div",I,[o("div",R,[a[2]||(a[2]=o("h2",{class:"text-lg font-semibold text-foreground flex-1"},"Live Feed",-1)),o("div",M,[o("div",{class:b(["h-2 w-2 rounded-full",i(r)?"bg-emerald-500 animate-pulse":"bg-red-500"])},null,2),o("span",P,v(i(r)?"Connected":"Disconnected"),1)]),i(r)?k("",!0):(c(),$(N,{key:0,variant:"outline",size:"sm",onClick:i(p)},{default:x(()=>[...a[0]||(a[0]=[w(" Reconnect ",-1)])]),_:1},8,["onClick"])),C(N,{variant:"ghost",size:"sm",onClick:i(_)},{default:x(()=>[...a[1]||(a[1]=[w(" Clear ",-1)])]),_:1},8,["onClick"])]),i(u)&&!i(r)?(c(),f("div",W,v(i(u)),1)):k("",!0),C(A,{class:"flex-1 overflow-hidden"},{default:x(()=>[C(D,{class:"p-0 h-full"},{default:x(()=>[h.value.length===0?(c(),f("div",q,[...a[3]||(a[3]=[o("div",{class:"text-center"},[o("div",{class:"text-2xl mb-2"},"📡"),o("p",null,"Waiting for events..."),o("p",{class:"text-xs mt-1"},"Activity will appear here in real-time")],-1)])])):(c(),f("div",G,[(c(!0),f(B,null,F(h.value,(d,L)=>(c(),f("div",{key:L,class:"flex items-start gap-2 px-4 py-1.5 hover:bg-muted/50 border-b border-border/30"},[o("span",{class:b([n(d.type),"shrink-0 mt-0.5"])},v(g(d.type)),3),o("div",H,[o("div",K,[o("span",{class:b([n(d.type),"font-medium"])},v(d.type),3),S(d.data)?(c(),f("span",Q,v(S(d.data)),1)):k("",!0)]),o("p",X,v(j(d.data)),1)])]))),128))]))]),_:1})]),_:1})]))}});export{se as default};
import{E as T,r as y,d as J,u as O,x as V,c as f,a as o,p as b,i,t as v,n as $,w as x,j as k,e as C,o as c,k as w,F as B,l as F,m as z}from"./index-DBJT1cIA.js";import{_ as A,a as D}from"./CardContent.vue_vue_type_script_setup_true_lang-C1186bj-.js";import{_ as N}from"./Button.vue_vue_type_script_setup_true_lang-CBskCLXi.js";import"./utils-1boGOziL.js";function U(E){const e=y([]),l=y(!1),m=y(null);let s=null,r=null,u=!1;function p(){if(!u)try{s=new EventSource(E),s.onopen=()=>{l.value=!0,m.value=null},s.onmessage=n=>{try{const g=JSON.parse(n.data);e.value.push({type:"message",data:g}),e.value.length>200&&e.value.shift()}catch{e.value.push({type:"message",data:n.data})}},s.addEventListener("session_start",n=>{try{e.value.push({type:"session_start",data:JSON.parse(n.data)})}catch{e.value.push({type:"session_start",data:n.data})}e.value.length>200&&e.value.shift()}),s.addEventListener("session_end",n=>{try{e.value.push({type:"session_end",data:JSON.parse(n.data)})}catch{e.value.push({type:"session_end",data:n.data})}e.value.length>200&&e.value.shift()}),s.addEventListener("activity",n=>{try{e.value.push({type:"activity",data:JSON.parse(n.data)})}catch{e.value.push({type:"activity",data:n.data})}e.value.length>200&&e.value.shift()}),s.onerror=()=>{l.value=!1,m.value="Connection lost, reconnecting...",s==null||s.close(),s=null,u||(r=setTimeout(()=>p(),5e3))}}catch{m.value="Failed to connect to event stream",u||(r=setTimeout(()=>p(),5e3))}}function _(){u=!0,r&&clearTimeout(r),s==null||s.close(),s=null,l.value=!1}function h(){e.value=[]}return T(()=>{_()}),{events:e,connected:l,error:m,connect:p,disconnect:_,clearEvents:h}}const I={class:"p-6 h-full flex flex-col"},R={class:"flex items-center gap-3 mb-4"},M={class:"flex items-center gap-2"},P={class:"text-xs text-muted-foreground"},W={key:0,class:"mb-4 text-xs text-amber-400 bg-amber-500/10 border border-amber-500/30 rounded px-3 py-2"},q={key:0,class:"flex items-center justify-center h-full text-sm text-muted-foreground"},G={key:1,class:"overflow-y-auto h-full font-mono text-xs"},H={class:"flex-1 min-w-0"},K={class:"flex items-center gap-2 flex-wrap"},Q={key:0,class:"text-muted-foreground"},X={class:"text-muted-foreground truncate mt-0.5"},se=J({__name:"LiveView",setup(E){const e=O(),l=e.getToken(),m=`/cc-dashboard/api/events${l?`?token=${encodeURIComponent(l)}`:""}`,{events:s,connected:r,error:u,connect:p,clearEvents:_}=U(m);V(()=>{e.isAuthenticated&&l&&p()});const h=z(()=>[...s.value].reverse().slice(0,100));function n(t){return t==="session_start"?"text-emerald-400":t==="session_end"?"text-amber-400":t==="activity"?"text-blue-400":"text-muted-foreground"}function g(t){return t==="session_start"?"▶":t==="session_end"?"■":t==="activity"?"●":"○"}function j(t){if(typeof t=="string")return t;if(t&&typeof t=="object"){const a=t;return a.message||a.summary||JSON.stringify(t)}return String(t)}function S(t){if(t&&typeof t=="object"){const a=t;return a.display_name||a.project_id||""}return""}return(t,a)=>(c(),f("div",I,[o("div",R,[a[2]||(a[2]=o("h2",{class:"text-lg font-semibold text-foreground flex-1"},"Live Feed",-1)),o("div",M,[o("div",{class:b(["h-2 w-2 rounded-full",i(r)?"bg-emerald-500 animate-pulse":"bg-red-500"])},null,2),o("span",P,v(i(r)?"Connected":"Disconnected"),1)]),i(r)?k("",!0):(c(),$(N,{key:0,variant:"outline",size:"sm",onClick:i(p)},{default:x(()=>[...a[0]||(a[0]=[w(" Reconnect ",-1)])]),_:1},8,["onClick"])),C(N,{variant:"ghost",size:"sm",onClick:i(_)},{default:x(()=>[...a[1]||(a[1]=[w(" Clear ",-1)])]),_:1},8,["onClick"])]),i(u)&&!i(r)?(c(),f("div",W,v(i(u)),1)):k("",!0),C(A,{class:"flex-1 overflow-hidden"},{default:x(()=>[C(D,{class:"p-0 h-full"},{default:x(()=>[h.value.length===0?(c(),f("div",q,[...a[3]||(a[3]=[o("div",{class:"text-center"},[o("div",{class:"text-2xl mb-2"},"📡"),o("p",null,"Waiting for events..."),o("p",{class:"text-xs mt-1"},"Activity will appear here in real-time")],-1)])])):(c(),f("div",G,[(c(!0),f(B,null,F(h.value,(d,L)=>(c(),f("div",{key:L,class:"flex items-start gap-2 px-4 py-1.5 hover:bg-muted/50 border-b border-border/30"},[o("span",{class:b([n(d.type),"shrink-0 mt-0.5"])},v(g(d.type)),3),o("div",H,[o("div",K,[o("span",{class:b([n(d.type),"font-medium"])},v(d.type),3),S(d.data)?(c(),f("span",Q,v(S(d.data)),1)):k("",!0)]),o("p",X,v(j(d.data)),1)])]))),128))]))]),_:1})]),_:1})]))}});export{se as default};

View file

@ -1 +1 @@
import{d as g,u as b,c as u,a as s,b as _,e as a,w as i,o as m,f as h,g as w,h as y,i as o,t as V,j as k,k as C,r as c}from"./index-B9hhyP-T.js";import{_ as S}from"./Button.vue_vue_type_script_setup_true_lang-mCZU1D3b.js";import{_ as p}from"./Input.vue_vue_type_script_setup_true_lang-CBtApgd6.js";import{_ as N,a as j}from"./CardContent.vue_vue_type_script_setup_true_lang-B1uPgvNK.js";import"./utils-DuVQys2y.js";const B={class:"min-h-screen flex items-center justify-center bg-background p-4"},$={class:"w-full max-w-sm"},q={key:0,class:"rounded-md bg-destructive/10 border border-destructive/30 px-3 py-2 text-sm text-destructive"},z={class:"space-y-1.5"},D={class:"space-y-1.5"},U=g({__name:"LoginView",setup(E){const f=h(),v=w(),t=b(),r=c(""),l=c("");async function x(){try{await t.login(r.value,l.value);const n=v.query.redirect;f.push(n??"/")}catch{}}return(n,e)=>(m(),u("div",B,[s("div",$,[e[5]||(e[5]=_('<div class="text-center mb-8"><div class="inline-flex h-12 w-12 items-center justify-center rounded-xl bg-primary mb-3"><svg class="h-7 w-7 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"></path></svg></div><h1 class="text-2xl font-bold text-foreground">CC Dashboard</h1><p class="text-sm text-muted-foreground mt-1">Corporate Planning Hub</p></div>',1)),a(N,null,{default:i(()=>[a(j,{class:"pt-6"},{default:i(()=>[s("form",{class:"space-y-4",onSubmit:y(x,["prevent"])},[o(t).error?(m(),u("div",q,V(o(t).error),1)):k("",!0),s("div",z,[e[2]||(e[2]=s("label",{for:"email",class:"text-sm font-medium text-foreground"},"Email",-1)),a(p,{id:"email",modelValue:r.value,"onUpdate:modelValue":e[0]||(e[0]=d=>r.value=d),type:"email",placeholder:"you@company.com",autocomplete:"email",disabled:o(t).loading,required:""},null,8,["modelValue","disabled"])]),s("div",D,[e[3]||(e[3]=s("label",{for:"password",class:"text-sm font-medium text-foreground"},"Password",-1)),a(p,{id:"password",modelValue:l.value,"onUpdate:modelValue":e[1]||(e[1]=d=>l.value=d),type:"password",placeholder:"••••••••",autocomplete:"current-password",disabled:o(t).loading,required:""},null,8,["modelValue","disabled"])]),a(S,{type:"submit",class:"w-full",loading:o(t).loading},{default:i(()=>[...e[4]||(e[4]=[C(" Sign in ",-1)])]),_:1},8,["loading"])],32)]),_:1})]),_:1})])]))}});export{U as default};
import{d as g,u as b,c as u,a as s,b as _,e as a,w as i,o as m,f as h,g as w,h as y,i as o,t as V,j as k,k as C,r as c}from"./index-DBJT1cIA.js";import{_ as S}from"./Button.vue_vue_type_script_setup_true_lang-CBskCLXi.js";import{_ as p}from"./Input.vue_vue_type_script_setup_true_lang-C-EOU0Fh.js";import{_ as N,a as j}from"./CardContent.vue_vue_type_script_setup_true_lang-C1186bj-.js";import"./utils-1boGOziL.js";const B={class:"min-h-screen flex items-center justify-center bg-background p-4"},$={class:"w-full max-w-sm"},q={key:0,class:"rounded-md bg-destructive/10 border border-destructive/30 px-3 py-2 text-sm text-destructive"},z={class:"space-y-1.5"},D={class:"space-y-1.5"},U=g({__name:"LoginView",setup(E){const f=h(),v=w(),t=b(),r=c(""),l=c("");async function x(){try{await t.login(r.value,l.value);const n=v.query.redirect;f.push(n??"/")}catch{}}return(n,e)=>(m(),u("div",B,[s("div",$,[e[5]||(e[5]=_('<div class="text-center mb-8"><div class="inline-flex h-12 w-12 items-center justify-center rounded-xl bg-primary mb-3"><svg class="h-7 w-7 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"></path></svg></div><h1 class="text-2xl font-bold text-foreground">CC Dashboard</h1><p class="text-sm text-muted-foreground mt-1">Corporate Planning Hub</p></div>',1)),a(N,null,{default:i(()=>[a(j,{class:"pt-6"},{default:i(()=>[s("form",{class:"space-y-4",onSubmit:y(x,["prevent"])},[o(t).error?(m(),u("div",q,V(o(t).error),1)):k("",!0),s("div",z,[e[2]||(e[2]=s("label",{for:"email",class:"text-sm font-medium text-foreground"},"Email",-1)),a(p,{id:"email",modelValue:r.value,"onUpdate:modelValue":e[0]||(e[0]=d=>r.value=d),type:"email",placeholder:"you@company.com",autocomplete:"email",disabled:o(t).loading,required:""},null,8,["modelValue","disabled"])]),s("div",D,[e[3]||(e[3]=s("label",{for:"password",class:"text-sm font-medium text-foreground"},"Password",-1)),a(p,{id:"password",modelValue:l.value,"onUpdate:modelValue":e[1]||(e[1]=d=>l.value=d),type:"password",placeholder:"••••••••",autocomplete:"current-password",disabled:o(t).loading,required:""},null,8,["modelValue","disabled"])]),a(S,{type:"submit",class:"w-full",loading:o(t).loading},{default:i(()=>[...e[4]||(e[4]=[C(" Sign in ",-1)])]),_:1},8,["loading"])],32)]),_:1})]),_:1})])]))}});export{U as default};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1 +1 @@
import{c as r}from"./utils-DuVQys2y.js";import{d as s,o as n,c as t,p as l,i as c,a as d,A as u}from"./index-B9hhyP-T.js";const h=s({__name:"Progress",props:{value:{},max:{default:100},class:{},color:{default:"default"}},setup(a){const e=a,o=()=>Math.min(100,Math.max(0,e.value/e.max*100));return(i,m)=>(n(),t("div",{class:l(c(r)("relative h-2 w-full overflow-hidden rounded-full bg-secondary",e.class))},[d("div",{class:l(["h-full rounded-full transition-all duration-300",{"bg-primary":a.color==="default","bg-emerald-500":a.color==="success","bg-amber-500":a.color==="warning","bg-red-500":a.color==="danger"}]),style:u({width:`${o()}%`})},null,6)],2))}});export{h as _};
import{c as r}from"./utils-1boGOziL.js";import{d as s,o as n,c as t,p as l,i as c,a as d,A as u}from"./index-DBJT1cIA.js";const h=s({__name:"Progress",props:{value:{},max:{default:100},class:{},color:{default:"default"}},setup(a){const e=a,o=()=>Math.min(100,Math.max(0,e.value/e.max*100));return(i,m)=>(n(),t("div",{class:l(c(r)("relative h-2 w-full overflow-hidden rounded-full bg-secondary",e.class))},[d("div",{class:l(["h-full rounded-full transition-all duration-300",{"bg-primary":a.color==="default","bg-emerald-500":a.color==="success","bg-amber-500":a.color==="warning","bg-red-500":a.color==="danger"}]),style:u({width:`${o()}%`})},null,6)],2))}});export{h as _};

View file

@ -1 +0,0 @@
import{d as $,x as N,c as t,e as l,F as u,a as o,t as n,j as c,i as _,w as r,g as D,r as b,o as e,k as m,l as x,A as k}from"./index-B9hhyP-T.js";import{d as T}from"./dashboard-DFJs0AgU.js";import{_ as f,a as p}from"./CardContent.vue_vue_type_script_setup_true_lang-B1uPgvNK.js";import{_ as h,a as v}from"./CardTitle.vue_vue_type_script_setup_true_lang-Twj0MqtU.js";import{f as y,_ as V,b as F}from"./utils-DuVQys2y.js";const A={class:"p-6"},B={key:0,class:"flex items-center justify-center h-40"},C={class:"mb-6"},R={class:"flex items-start justify-between gap-4 flex-wrap"},S={class:"text-xl font-bold text-foreground"},z={class:"flex items-center gap-3 mt-1 flex-wrap"},M={key:0,class:"text-sm text-muted-foreground"},P={key:1,class:"text-xs bg-muted text-muted-foreground px-2 py-1 rounded"},E=["href"],H={class:"text-right"},I={class:"text-2xl font-bold text-foreground"},L={class:"h-32 flex items-end gap-px"},U=["title"],q={class:"grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6"},G={key:0,class:"text-sm text-muted-foreground"},J={key:1,class:"space-y-1.5"},K=["title"],O={class:"text-foreground shrink-0 ml-2"},Q={key:0,class:"text-sm text-muted-foreground"},W={key:1,class:"space-y-2"},X={class:"text-xs text-foreground w-24 truncate shrink-0"},Y={class:"flex-1 h-2 bg-secondary rounded-full overflow-hidden"},Z={class:"text-xs text-muted-foreground w-8 text-right shrink-0"},tt={key:0,class:"text-sm text-muted-foreground"},et={key:1,class:"space-y-2"},st={class:"flex-1 min-w-0"},ot={class:"text-xs text-foreground"},at={key:0,class:"text-xs text-muted-foreground mt-0.5 line-clamp-2"},lt={class:"text-right shrink-0"},rt={class:"text-xs font-medium text-foreground"},dt={class:"text-xs text-muted-foreground"},nt={key:2,class:"text-center text-muted-foreground py-12"},pt=$({__name:"ProjectDetailView",setup(it){const w=D().params.id,a=b(null),g=b(!1);N(async()=>{g.value=!0;try{const i=await T.project(w);a.value=i.data}finally{g.value=!1}});const j=()=>{var i;return Math.max(...((i=a.value)==null?void 0:i.timeline.map(d=>d.hours))??[1],1)};return(i,d)=>(e(),t("div",A,[g.value?(e(),t("div",B,[l(V,{size:"lg",class:"text-primary"})])):a.value?(e(),t(u,{key:1},[o("div",C,[o("div",R,[o("div",null,[o("h2",S,n(a.value.display_name),1),o("div",z,[a.value.client?(e(),t("span",M,n(a.value.client),1)):c("",!0),a.value.job_number?(e(),t("span",P,n(a.value.job_number),1)):c("",!0),a.value.repo_url?(e(),t("a",{key:2,href:a.value.repo_url,target:"_blank",class:"text-xs text-primary hover:underline"}," Repository → ",8,E)):c("",!0)])]),o("div",H,[o("p",I,n(_(y)(a.value.total_hours)),1),d[0]||(d[0]=o("p",{class:"text-xs text-muted-foreground"},"total hours",-1))])])]),l(f,{class:"mb-6"},{default:r(()=>[l(h,{class:"pb-2"},{default:r(()=>[l(v,{class:"text-sm"},{default:r(()=>[...d[1]||(d[1]=[m("Daily Activity",-1)])]),_:1})]),_:1}),l(p,null,{default:r(()=>[o("div",L,[(e(!0),t(u,null,x(a.value.timeline,s=>(e(),t("div",{key:s.date,class:"flex-1 bg-primary/70 hover:bg-primary rounded-t transition-colors",style:k({height:`${s.hours/j()*100}%`}),title:`${s.date}: ${_(y)(s.hours)}`},null,12,U))),128))])]),_:1})]),_:1}),o("div",q,[l(f,null,{default:r(()=>[l(h,{class:"pb-2"},{default:r(()=>[l(v,{class:"text-sm"},{default:r(()=>[...d[2]||(d[2]=[m("Top Files",-1)])]),_:1})]),_:1}),l(p,null,{default:r(()=>[a.value.top_files.length?(e(),t("div",J,[(e(!0),t(u,null,x(a.value.top_files.slice(0,10),s=>(e(),t("div",{key:s.path,class:"flex items-center justify-between text-xs"},[o("span",{class:"text-muted-foreground truncate max-w-[200px]",title:s.path},n(s.path.split("/").pop()),9,K),o("span",O,n(s.count)+"×",1)]))),128))])):(e(),t("div",G,"No data"))]),_:1})]),_:1}),l(f,null,{default:r(()=>[l(h,{class:"pb-2"},{default:r(()=>[l(v,{class:"text-sm"},{default:r(()=>[...d[3]||(d[3]=[m("Tool Usage",-1)])]),_:1})]),_:1}),l(p,null,{default:r(()=>[a.value.top_tools.length?(e(),t("div",W,[(e(!0),t(u,null,x(a.value.top_tools.slice(0,8),s=>(e(),t("div",{key:s.tool,class:"flex items-center gap-2"},[o("span",X,n(s.tool),1),o("div",Y,[o("div",{class:"h-full bg-primary rounded-full",style:k({width:`${s.pct}%`})},null,4)]),o("span",Z,n(s.pct.toFixed(0))+"% ",1)]))),128))])):(e(),t("div",Q,"No data"))]),_:1})]),_:1})]),l(f,null,{default:r(()=>[l(h,{class:"pb-2"},{default:r(()=>[l(v,{class:"text-sm"},{default:r(()=>[...d[4]||(d[4]=[m("Recent Sessions",-1)])]),_:1})]),_:1}),l(p,null,{default:r(()=>[a.value.sessions.length?(e(),t("div",et,[(e(!0),t(u,null,x(a.value.sessions.slice(0,50),s=>(e(),t("div",{key:s.id,class:"flex items-start gap-3 py-2 border-b border-border last:border-0"},[o("div",st,[o("p",ot,n(_(F)(s.start_at)),1),s.summary?(e(),t("p",at,n(s.summary),1)):c("",!0)]),o("div",lt,[o("p",rt,n(_(y)(s.duration_hours)),1),o("p",dt,n(s.commit_count)+" commits ",1)])]))),128))])):(e(),t("div",tt,"No sessions"))]),_:1})]),_:1})],64)):(e(),t("div",nt," Project not found "))]))}});export{pt as default};

File diff suppressed because one or more lines are too long

View file

@ -1 +1 @@
import{d as p,x as g,c as r,a as s,e as d,F as v,l as y,r as _,o,n as h,w as f,t as a,j as i,i as u,p as b,f as k}from"./index-B9hhyP-T.js";import{d as w}from"./dashboard-DFJs0AgU.js";import{a as C,_ as $}from"./CardContent.vue_vue_type_script_setup_true_lang-B1uPgvNK.js";import{_ as B}from"./Progress.vue_vue_type_script_setup_true_lang-o01-BFVV.js";import{_ as N,f as V,a as D}from"./utils-DuVQys2y.js";const F={class:"p-6"},j={key:0,class:"flex items-center justify-center h-40"},z={key:1,class:"text-center text-muted-foreground py-12"},L={key:2,class:"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4"},P={class:"flex items-start justify-between gap-2 mb-3"},S={class:"min-w-0"},A={class:"font-semibold text-sm text-foreground truncate"},E={key:0,class:"text-xs text-muted-foreground truncate"},M={key:0,class:"text-xs bg-muted text-muted-foreground px-1.5 py-0.5 rounded shrink-0"},R={class:"space-y-1.5"},T={class:"flex items-center justify-between text-xs"},q={class:"font-medium text-foreground"},G={class:"flex items-center justify-between text-xs"},H={class:"text-foreground"},I={key:0,class:"flex items-center justify-between text-xs"},J={class:"text-foreground"},K={key:0,class:"mt-3"},O={class:"flex items-center justify-between text-xs mb-1"},st=p({__name:"ProjectsView",setup(Q){const m=k(),l=_([]),c=_(!1);g(async()=>{c.value=!0;try{const n=await w.projects({});l.value=n.data.sort((e,t)=>t.total_hours-e.total_hours)}finally{c.value=!1}});const x=n=>n?n>90?"danger":n>70?"warning":"success":"default";return(n,e)=>(o(),r("div",F,[e[4]||(e[4]=s("h2",{class:"text-lg font-semibold text-foreground mb-6"},"Projects",-1)),c.value?(o(),r("div",j,[d(N,{size:"lg",class:"text-primary"})])):l.value.length===0?(o(),r("div",z," No projects found ")):(o(),r("div",L,[(o(!0),r(v,null,y(l.value,t=>(o(),h($,{key:t.project_id,class:"cursor-pointer hover:border-primary/50 transition-colors",onClick:U=>u(m).push(`/projects/${t.project_id}`)},{default:f(()=>[d(C,{class:"p-4"},{default:f(()=>[s("div",P,[s("div",S,[s("p",A,a(t.display_name),1),t.client?(o(),r("p",E,a(t.client),1)):i("",!0)]),t.job_number?(o(),r("span",M,a(t.job_number),1)):i("",!0)]),s("div",R,[s("div",T,[e[0]||(e[0]=s("span",{class:"text-muted-foreground"},"Total hours",-1)),s("span",q,a(u(V)(t.total_hours)),1)]),s("div",G,[e[1]||(e[1]=s("span",{class:"text-muted-foreground"},"Sessions",-1)),s("span",H,a(t.session_count),1)]),t.last_active?(o(),r("div",I,[e[2]||(e[2]=s("span",{class:"text-muted-foreground"},"Last active",-1)),s("span",J,a(u(D)(t.last_active)),1)])):i("",!0)]),t.progress_pct!==null?(o(),r("div",K,[s("div",O,[e[3]||(e[3]=s("span",{class:"text-muted-foreground"},"Budget",-1)),s("span",{class:b(t.progress_pct>90?"text-red-400":"text-muted-foreground")},a(t.progress_pct.toFixed(0))+"% ",3)]),d(B,{value:t.progress_pct,color:x(t.progress_pct)},null,8,["value","color"])])):i("",!0)]),_:2},1024)]),_:2},1032,["onClick"]))),128))]))]))}});export{st as default};
import{d as p,x as g,c as r,a as s,e as d,F as v,l as y,r as _,o,n as h,w as f,t as a,j as i,i as u,p as b,f as k}from"./index-DBJT1cIA.js";import{d as w}from"./dashboard-CTV-aZaN.js";import{a as C,_ as $}from"./CardContent.vue_vue_type_script_setup_true_lang-C1186bj-.js";import{_ as B}from"./Progress.vue_vue_type_script_setup_true_lang-togGnoYz.js";import{_ as N,f as V,a as D}from"./utils-1boGOziL.js";const F={class:"p-6"},j={key:0,class:"flex items-center justify-center h-40"},z={key:1,class:"text-center text-muted-foreground py-12"},L={key:2,class:"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4"},P={class:"flex items-start justify-between gap-2 mb-3"},S={class:"min-w-0"},A={class:"font-semibold text-sm text-foreground truncate"},E={key:0,class:"text-xs text-muted-foreground truncate"},M={key:0,class:"text-xs bg-muted text-muted-foreground px-1.5 py-0.5 rounded shrink-0"},R={class:"space-y-1.5"},T={class:"flex items-center justify-between text-xs"},q={class:"font-medium text-foreground"},G={class:"flex items-center justify-between text-xs"},H={class:"text-foreground"},I={key:0,class:"flex items-center justify-between text-xs"},J={class:"text-foreground"},K={key:0,class:"mt-3"},O={class:"flex items-center justify-between text-xs mb-1"},st=p({__name:"ProjectsView",setup(Q){const m=k(),l=_([]),c=_(!1);g(async()=>{c.value=!0;try{const n=await w.projects({});l.value=n.data.sort((e,t)=>t.total_hours-e.total_hours)}finally{c.value=!1}});const x=n=>n?n>90?"danger":n>70?"warning":"success":"default";return(n,e)=>(o(),r("div",F,[e[4]||(e[4]=s("h2",{class:"text-lg font-semibold text-foreground mb-6"},"Projects",-1)),c.value?(o(),r("div",j,[d(N,{size:"lg",class:"text-primary"})])):l.value.length===0?(o(),r("div",z," No projects found ")):(o(),r("div",L,[(o(!0),r(v,null,y(l.value,t=>(o(),h($,{key:t.project_id,class:"cursor-pointer hover:border-primary/50 transition-colors",onClick:U=>u(m).push(`/projects/${t.project_id}`)},{default:f(()=>[d(C,{class:"p-4"},{default:f(()=>[s("div",P,[s("div",S,[s("p",A,a(t.display_name),1),t.client?(o(),r("p",E,a(t.client),1)):i("",!0)]),t.job_number?(o(),r("span",M,a(t.job_number),1)):i("",!0)]),s("div",R,[s("div",T,[e[0]||(e[0]=s("span",{class:"text-muted-foreground"},"Total hours",-1)),s("span",q,a(u(V)(t.total_hours)),1)]),s("div",G,[e[1]||(e[1]=s("span",{class:"text-muted-foreground"},"Sessions",-1)),s("span",H,a(t.session_count),1)]),t.last_active?(o(),r("div",I,[e[2]||(e[2]=s("span",{class:"text-muted-foreground"},"Last active",-1)),s("span",J,a(u(D)(t.last_active)),1)])):i("",!0)]),t.progress_pct!==null?(o(),r("div",K,[s("div",O,[e[3]||(e[3]=s("span",{class:"text-muted-foreground"},"Budget",-1)),s("span",{class:b(t.progress_pct>90?"text-red-400":"text-muted-foreground")},a((t.progress_pct??0).toFixed(0))+"% ",3)]),d(B,{value:t.progress_pct,color:x(t.progress_pct)},null,8,["value","color"])])):i("",!0)]),_:2},1024)]),_:2},1032,["onClick"]))),128))]))]))}});export{st as default};

View file

@ -1,4 +1,4 @@
var Ce=Object.defineProperty;var ae=a=>{throw TypeError(a)};var Ee=(a,t,e)=>t in a?Ce(a,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):a[t]=e;var k=(a,t,e)=>Ee(a,typeof t!="symbol"?t+"":t,e),Le=(a,t,e)=>t.has(a)||ae("Cannot "+e);var ce=(a,t,e)=>t.has(a)?ae("Cannot add the same private member more than once"):t instanceof WeakSet?t.add(a):t.set(a,e);var Z=(a,t,e)=>(Le(a,t,"access private method"),e);import{D as V,d as Be,x as qe,c as z,a as x,p as U,e as P,w as I,F as Ze,l as Pe,r as A,o as $,k as G,n as pe,t as W,i as De,j as he,K as ue,_ as Me}from"./index-B9hhyP-T.js";import{a as Qe,_ as je}from"./CardContent.vue_vue_type_script_setup_true_lang-B1uPgvNK.js";import{_ as fe}from"./Badge.vue_vue_type_script_setup_true_lang-BV0smx_q.js";import{_ as Ne}from"./Button.vue_vue_type_script_setup_true_lang-mCZU1D3b.js";import{_ as Oe,a as He,i as Fe}from"./utils-DuVQys2y.js";const ge={list:()=>V.get("/api/reports"),get:a=>V.get(`/api/reports/${a}`),generate:a=>V.post("/api/reports/generate",a)};function J(){return{async:!1,breaks:!1,extensions:null,gfm:!0,hooks:null,pedantic:!1,renderer:null,silent:!1,tokenizer:null,walkTokens:null}}let S=J();function we(a){S=a}const ye=/[&<>"']/,Ve=new RegExp(ye.source,"g"),$e=/[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/,Ue=new RegExp($e.source,"g"),Ge={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"},de=a=>Ge[a];function m(a,t){if(t){if(ye.test(a))return a.replace(Ve,de)}else if($e.test(a))return a.replace(Ue,de);return a}const We=/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/ig;function Xe(a){return a.replace(We,(t,e)=>(e=e.toLowerCase(),e==="colon"?":":e.charAt(0)==="#"?e.charAt(1)==="x"?String.fromCharCode(parseInt(e.substring(2),16)):String.fromCharCode(+e.substring(1)):""))}const Ke=/(^|[^\[])\^/g;function d(a,t){let e=typeof a=="string"?a:a.source;t=t||"";const n={replace:(i,r)=>{let s=typeof r=="string"?r:r.source;return s=s.replace(Ke,"$1"),e=e.replace(i,s),n},getRegex:()=>new RegExp(e,t)};return n}function ke(a){try{a=encodeURI(a).replace(/%25/g,"%")}catch{return null}return a}const E={exec:()=>null};function xe(a,t){const e=a.replace(/\|/g,(r,s,l)=>{let o=!1,u=s;for(;--u>=0&&l[u]==="\\";)o=!o;return o?"|":" |"}),n=e.split(/ \|/);let i=0;if(n[0].trim()||n.shift(),n.length>0&&!n[n.length-1].trim()&&n.pop(),t)if(n.length>t)n.splice(t);else for(;n.length<t;)n.push("");for(;i<n.length;i++)n[i]=n[i].trim().replace(/\\\|/g,"|");return n}function D(a,t,e){const n=a.length;if(n===0)return"";let i=0;for(;i<n&&a.charAt(n-i-1)===t;)i++;return a.slice(0,n-i)}function Je(a,t){if(a.indexOf(t[1])===-1)return-1;let e=0;for(let n=0;n<a.length;n++)if(a[n]==="\\")n++;else if(a[n]===t[0])e++;else if(a[n]===t[1]&&(e--,e<0))return n;return-1}function me(a,t,e,n){const i=t.href,r=t.title?m(t.title):null,s=a[1].replace(/\\([\[\]])/g,"$1");if(a[0].charAt(0)!=="!"){n.state.inLink=!0;const l={type:"link",raw:e,href:i,title:r,text:s,tokens:n.inlineTokens(s)};return n.state.inLink=!1,l}return{type:"image",raw:e,href:i,title:r,text:m(s)}}function Ye(a,t){const e=a.match(/^(\s+)(?:```)/);if(e===null)return t;const n=e[1];return t.split(`
var Ce=Object.defineProperty;var ae=a=>{throw TypeError(a)};var Ee=(a,t,e)=>t in a?Ce(a,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):a[t]=e;var k=(a,t,e)=>Ee(a,typeof t!="symbol"?t+"":t,e),Le=(a,t,e)=>t.has(a)||ae("Cannot "+e);var ce=(a,t,e)=>t.has(a)?ae("Cannot add the same private member more than once"):t instanceof WeakSet?t.add(a):t.set(a,e);var Z=(a,t,e)=>(Le(a,t,"access private method"),e);import{D as V,d as Be,x as qe,c as z,a as x,p as U,e as P,w as I,F as Ze,l as Pe,r as A,o as $,k as G,n as pe,t as W,i as De,j as he,K as ue,_ as Me}from"./index-DBJT1cIA.js";import{a as Qe,_ as je}from"./CardContent.vue_vue_type_script_setup_true_lang-C1186bj-.js";import{_ as fe}from"./Badge.vue_vue_type_script_setup_true_lang-DWhAdLQt.js";import{_ as Ne}from"./Button.vue_vue_type_script_setup_true_lang-CBskCLXi.js";import{_ as Oe,a as He,i as Fe}from"./utils-1boGOziL.js";const ge={list:()=>V.get("/api/reports"),get:a=>V.get(`/api/reports/${a}`),generate:a=>V.post("/api/reports/generate",a)};function J(){return{async:!1,breaks:!1,extensions:null,gfm:!0,hooks:null,pedantic:!1,renderer:null,silent:!1,tokenizer:null,walkTokens:null}}let S=J();function we(a){S=a}const ye=/[&<>"']/,Ve=new RegExp(ye.source,"g"),$e=/[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/,Ue=new RegExp($e.source,"g"),Ge={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"},de=a=>Ge[a];function m(a,t){if(t){if(ye.test(a))return a.replace(Ve,de)}else if($e.test(a))return a.replace(Ue,de);return a}const We=/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/ig;function Xe(a){return a.replace(We,(t,e)=>(e=e.toLowerCase(),e==="colon"?":":e.charAt(0)==="#"?e.charAt(1)==="x"?String.fromCharCode(parseInt(e.substring(2),16)):String.fromCharCode(+e.substring(1)):""))}const Ke=/(^|[^\[])\^/g;function d(a,t){let e=typeof a=="string"?a:a.source;t=t||"";const n={replace:(i,r)=>{let s=typeof r=="string"?r:r.source;return s=s.replace(Ke,"$1"),e=e.replace(i,s),n},getRegex:()=>new RegExp(e,t)};return n}function ke(a){try{a=encodeURI(a).replace(/%25/g,"%")}catch{return null}return a}const E={exec:()=>null};function xe(a,t){const e=a.replace(/\|/g,(r,s,l)=>{let o=!1,u=s;for(;--u>=0&&l[u]==="\\";)o=!o;return o?"|":" |"}),n=e.split(/ \|/);let i=0;if(n[0].trim()||n.shift(),n.length>0&&!n[n.length-1].trim()&&n.pop(),t)if(n.length>t)n.splice(t);else for(;n.length<t;)n.push("");for(;i<n.length;i++)n[i]=n[i].trim().replace(/\\\|/g,"|");return n}function D(a,t,e){const n=a.length;if(n===0)return"";let i=0;for(;i<n&&a.charAt(n-i-1)===t;)i++;return a.slice(0,n-i)}function Je(a,t){if(a.indexOf(t[1])===-1)return-1;let e=0;for(let n=0;n<a.length;n++)if(a[n]==="\\")n++;else if(a[n]===t[0])e++;else if(a[n]===t[1]&&(e--,e<0))return n;return-1}function me(a,t,e,n){const i=t.href,r=t.title?m(t.title):null,s=a[1].replace(/\\([\[\]])/g,"$1");if(a[0].charAt(0)!=="!"){n.state.inLink=!0;const l={type:"link",raw:e,href:i,title:r,text:s,tokens:n.inlineTokens(s)};return n.state.inLink=!1,l}return{type:"image",raw:e,href:i,title:r,text:m(s)}}function Ye(a,t){const e=a.match(/^(\s+)(?:```)/);if(e===null)return t;const n=e[1];return t.split(`
`).map(i=>{const r=i.match(/^\s+/);if(r===null)return i;const[s]=r;return s.length>=n.length?i.slice(n.length):i}).join(`
`)}class Q{constructor(t){k(this,"options");k(this,"rules");k(this,"lexer");this.options=t||S}space(t){const e=this.rules.block.newline.exec(t);if(e&&e[0].length>0)return{type:"space",raw:e[0]}}code(t){const e=this.rules.block.code.exec(t);if(e){const n=e[0].replace(/^ {1,4}/gm,"");return{type:"code",raw:e[0],codeBlockStyle:"indented",text:this.options.pedantic?n:D(n,`
`)}}}fences(t){const e=this.rules.block.fences.exec(t);if(e){const n=e[0],i=Ye(n,e[3]||"");return{type:"code",raw:n,lang:e[2]?e[2].trim().replace(this.rules.inline.anyPunctuation,"$1"):e[2],text:i}}}heading(t){const e=this.rules.block.heading.exec(t);if(e){let n=e[2].trim();if(/#$/.test(n)){const i=D(n,"#");(this.options.pedantic||!i||/ $/.test(i))&&(n=i.trim())}return{type:"heading",raw:e[0],depth:e[1].length,text:n,tokens:this.lexer.inline(n)}}}hr(t){const e=this.rules.block.hr.exec(t);if(e)return{type:"hr",raw:e[0]}}blockquote(t){const e=this.rules.block.blockquote.exec(t);if(e){let n=e[0].replace(/\n {0,3}((?:=+|-+) *)(?=\n|$)/g,`

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1 +1 @@
import{D as e}from"./index-B9hhyP-T.js";const i={users:()=>e.get("/api/admin/users"),keys:()=>e.get("/api/keys"),createKey:s=>e.post("/api/keys",s),revokeKey:s=>e.delete(`/api/keys/${s}`)};export{i as a};
import{D as e}from"./index-DBJT1cIA.js";const i={users:()=>e.get("/api/admin/users"),keys:()=>e.get("/api/keys"),createKey:s=>e.post("/api/keys",s),revokeKey:s=>e.delete(`/api/keys/${s}`)};export{i as a};

View file

@ -1 +1 @@
import{D as t}from"./index-B9hhyP-T.js";const e={summary:a=>t.get("/api/dashboard/summary",{params:a}),projects:a=>t.get("/api/dashboard/projects",{params:a}),timeline:a=>t.get("/api/dashboard/timeline",{params:a}),monthly:a=>t.get("/api/dashboard/monthly",{params:a}),dow:a=>t.get("/api/dashboard/dow",{params:a}),tools:a=>t.get("/api/dashboard/tools",{params:a}),activity:a=>t.get("/api/dashboard/activity",{params:a}),calendar:a=>t.get("/api/dashboard/calendar",{params:a}),project:(a,o)=>t.get("/api/dashboard/project/"+a,{params:o})};export{e as d};
import{D as t}from"./index-DBJT1cIA.js";const e={summary:a=>t.get("/api/dashboard/summary",{params:a}),projects:a=>t.get("/api/dashboard/projects",{params:a}),timeline:a=>t.get("/api/dashboard/timeline",{params:a}),monthly:a=>t.get("/api/dashboard/monthly",{params:a}),dow:a=>t.get("/api/dashboard/dow",{params:a}),tools:a=>t.get("/api/dashboard/tools",{params:a}),activity:a=>t.get("/api/dashboard/activity",{params:a}),calendar:a=>t.get("/api/dashboard/calendar",{params:a}),project:(a,o)=>t.get("/api/dashboard/project/"+a,{params:o})};export{e as d};

View file

@ -1 +1 @@
import{D as s,B as I,r as o}from"./index-B9hhyP-T.js";const i={getIntegration:()=>s.get("/api/devops/integration"),saveIntegration:e=>s.put("/api/devops/integration",e),deleteIntegration:()=>s.delete("/api/devops/integration"),sync:()=>s.post("/api/devops/sync"),workItems:e=>s.get("/api/devops/work-items",{params:e?{state:e}:void 0})},m=I("devops",()=>{const e=o(null),r=o([]),l=o(!1),n=o(!1),c=o(null);async function u(){n.value=!0;try{const t=await i.getIntegration();e.value=t.data}catch{e.value=null}finally{n.value=!1}}async function d(t){const a=await i.saveIntegration(t);e.value=a.data}async function g(){await i.deleteIntegration(),e.value=null}async function f(){var t,a;l.value=!0,c.value=null;try{await i.sync(),await u()}catch(v){const p=v;throw c.value=((a=(t=p.response)==null?void 0:t.data)==null?void 0:a.detail)??p.message??"Sync failed",v}finally{l.value=!1}}async function y(t){n.value=!0;try{const a=await i.workItems(t);r.value=a.data}catch{r.value=[]}finally{n.value=!1}}return{integration:e,workItems:r,syncing:l,loading:n,error:c,fetchIntegration:u,saveIntegration:d,deleteIntegration:g,sync:f,fetchWorkItems:y}});export{m as u};
import{D as s,B as I,r as o}from"./index-DBJT1cIA.js";const i={getIntegration:()=>s.get("/api/devops/integration"),saveIntegration:e=>s.put("/api/devops/integration",e),deleteIntegration:()=>s.delete("/api/devops/integration"),sync:()=>s.post("/api/devops/sync"),workItems:e=>s.get("/api/devops/work-items",{params:e?{state:e}:void 0})},m=I("devops",()=>{const e=o(null),r=o([]),l=o(!1),n=o(!1),c=o(null);async function u(){n.value=!0;try{const t=await i.getIntegration();e.value=t.data}catch{e.value=null}finally{n.value=!1}}async function d(t){const a=await i.saveIntegration(t);e.value=a.data}async function g(){await i.deleteIntegration(),e.value=null}async function f(){var t,a;l.value=!0,c.value=null;try{await i.sync(),await u()}catch(v){const p=v;throw c.value=((a=(t=p.response)==null?void 0:t.data)==null?void 0:a.detail)??p.message??"Sync failed",v}finally{l.value=!1}}async function y(t){n.value=!0;try{const a=await i.workItems(t);r.value=a.data}catch{r.value=[]}finally{n.value=!1}}return{integration:e,workItems:r,syncing:l,loading:n,error:c,fetchIntegration:u,saveIntegration:d,deleteIntegration:g,sync:f,fetchWorkItems:y}});export{m as u};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,12 +1,21 @@
<!DOCTYPE html>
<html lang="en" class="dark">
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/cc-dashboard/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>CC Dashboard</title>
<script type="module" crossorigin src="/cc-dashboard/static/assets/index-B9hhyP-T.js"></script>
<link rel="stylesheet" crossorigin href="/cc-dashboard/static/assets/index-jWLq2uh_.css">
<script>
(function() {
var theme = localStorage.getItem('theme');
if (theme === 'light') { document.documentElement.classList.remove('dark'); }
else if (theme === 'dark') { document.documentElement.classList.add('dark'); }
else if (window.matchMedia('(prefers-color-scheme: dark)').matches) { document.documentElement.classList.add('dark'); }
else { document.documentElement.classList.remove('dark'); }
})();
</script>
<script type="module" crossorigin src="/cc-dashboard/static/assets/index-DBJT1cIA.js"></script>
<link rel="stylesheet" crossorigin href="/cc-dashboard/static/assets/index-BOPSulQG.css">
</head>
<body>
<div id="app"></div>

View file

@ -16,6 +16,7 @@ const props = defineProps<{
const emit = defineEmits<{
resizeStart: [event: MouseEvent]
click: [block: CalendarBlock]
blockDragStart: [block: CalendarBlock, event: DragEvent]
}>()
const effectiveEnd = computed(() =>
@ -58,8 +59,10 @@ const isShort = computed(() => effectiveHeight.value < 40)
'border-2 border-dashed opacity-80': block.kind === 'planned',
'border-2 calendar-block--manual': block.kind === 'manual',
}"
:draggable="block.kind === 'planned' && !!block.task_id"
:style="style"
@click="emit('click', block)"
@dragstart="block.kind === 'planned' && block.task_id ? emit('blockDragStart', block, $event) : undefined"
>
<!-- Content -->
<div class="px-1.5 py-1 h-full flex flex-col text-white overflow-hidden">

View file

@ -128,6 +128,7 @@ function formatHour(h: number): string {
:resize-end="isResizing(block) ? dnd.resizePreviewEnd.value : null"
@click="emit('blockClick', block)"
@resize-start="dnd.onResizeStart(block, $event)"
@block-drag-start="(b, ev) => dnd.onBlockDragStart(b, ev)"
/>
</div>
</div>

View file

@ -21,7 +21,7 @@ const props = withDefaults(
const emit = defineEmits<{
close: []
save: [payload: TaskCreatePayload | TaskUpdatePayload]
save: [payload: TaskCreatePayload | TaskUpdatePayload, block?: { start_at: string; end_at: string }]
}>()
const devopsStore = useDevopsStore()
@ -35,6 +35,8 @@ const form = ref({
title: '',
notes: '',
planned_date: '',
start_time: '',
end_time: '',
estimate_hours: 1,
status: 'todo' as Task['status'],
priority: 3 as Task['priority'],
@ -51,6 +53,8 @@ watch(
title: props.task.title,
notes: props.task.notes ?? '',
planned_date: props.task.planned_date ?? '',
start_time: '',
end_time: '',
estimate_hours: props.task.estimate_hours ?? 1,
status: props.task.status,
priority: props.task.priority,
@ -62,6 +66,8 @@ watch(
title: '',
notes: '',
planned_date: props.defaultDate ?? '',
start_time: '',
end_time: '',
estimate_hours: 1,
status: 'todo',
priority: 3,
@ -94,7 +100,16 @@ async function handleSave() {
project_id: form.value.project_id || null,
azure_work_item_id: form.value.azure_work_item_id || null,
}
emit('save', payload)
let block: { start_at: string; end_at: string } | undefined
if (form.value.planned_date && form.value.start_time && form.value.end_time) {
block = {
start_at: new Date(`${form.value.planned_date}T${form.value.start_time}:00`).toISOString(),
end_at: new Date(`${form.value.planned_date}T${form.value.end_time}:00`).toISOString(),
}
}
emit('save', payload, block)
} finally {
saving.value = false
}
@ -140,6 +155,18 @@ async function handleSave() {
</div>
</div>
<!-- Time slot row (optional) -->
<div class="grid grid-cols-2 gap-3">
<div class="space-y-1.5">
<label class="text-sm font-medium text-foreground">Start time <span class="text-muted-foreground font-normal">(optional)</span></label>
<Input v-model="form.start_time" type="time" :disabled="saving" />
</div>
<div class="space-y-1.5">
<label class="text-sm font-medium text-foreground">End time</label>
<Input v-model="form.end_time" type="time" :disabled="saving" />
</div>
</div>
<!-- Status + Priority row -->
<div class="grid grid-cols-2 gap-3">
<div class="space-y-1.5">

View file

@ -24,6 +24,16 @@ export function useCalendarDnD() {
e.dataTransfer?.setData('estimate_hours', String(task.estimate_hours ?? 1))
}
// --- Drag existing block to new time slot ---
function onBlockDragStart(block: CalendarBlock, e: DragEvent) {
const durationMs = new Date(block.end_at).getTime() - new Date(block.start_at).getTime()
e.dataTransfer?.setData('block_id', block.id)
e.dataTransfer?.setData('block_duration_ms', String(durationMs))
e.dataTransfer?.setData('task_id', block.task_id ?? '')
e.dataTransfer?.setData('estimate_hours', String(durationMs / 3600000))
}
function onDragOver(dayDate: Date, e: DragEvent) {
e.preventDefault()
dragOverDay.value = isoDateStr(dayDate)
@ -38,10 +48,10 @@ export function useCalendarDnD() {
dragOverDay.value = null
draggingTaskId.value = null
const blockId = e.dataTransfer?.getData('block_id')
const taskId = e.dataTransfer?.getData('task_id')
const estimateHours = parseFloat(e.dataTransfer?.getData('estimate_hours') ?? '1') || 1
if (!taskId) return
const blockDurationMs = parseFloat(e.dataTransfer?.getData('block_duration_ms') ?? '0')
// Calculate start time from Y position relative to the grid container
const target = e.currentTarget as HTMLElement
@ -49,16 +59,31 @@ export function useCalendarDnD() {
const relativeY = e.clientY - rect.top
const minutesFromDayStart = snapToGrid(relativeY / PX_PER_MIN, 15)
const clampedMinutes = Math.max(0, Math.min(minutesFromDayStart, 12 * 60)) // 7am - 7pm = 12 hours
const clampedMinutes = Math.max(0, Math.min(minutesFromDayStart, 12 * 60))
const startDate = new Date(dayDate)
startDate.setHours(DAY_START_HOUR, 0, 0, 0)
startDate.setMinutes(startDate.getMinutes() + clampedMinutes)
const start_at = startDate.toISOString()
if (blockId && blockDurationMs > 0) {
// Move existing block to new time slot
const endDate = new Date(startDate.getTime() + blockDurationMs)
const end_at = endDate.toISOString()
try {
await tasksStore.updateBlock(blockId, { start_at, end_at })
await calendarStore.fetchCurrentView()
} catch (err) {
console.error('Failed to move block:', err)
}
return
}
if (!taskId) return
const endDate = new Date(startDate)
endDate.setMinutes(endDate.getMinutes() + Math.round(estimateHours * 60))
const start_at = startDate.toISOString()
const end_at = endDate.toISOString()
// Optimistic update - create a temporary CalendarBlock
@ -83,10 +108,8 @@ export function useCalendarDnD() {
try {
await tasksStore.createBlock(taskId, { start_at, end_at })
// Refresh calendar to get real block data
await calendarStore.fetchCurrentView()
} catch (err) {
// Rollback optimistic update
calendarStore.removeBlock(tempId)
console.error('Failed to create task block:', err)
}
@ -162,6 +185,7 @@ export function useCalendarDnD() {
resizingBlock,
resizePreviewEnd,
onDragStart,
onBlockDragStart,
onDragOver,
onDragLeave,
onDrop,

View file

@ -44,13 +44,16 @@ function openEdit(task: Task) {
showForm.value = true
}
async function handleSave(payload: TaskCreatePayload | TaskUpdatePayload) {
async function handleSave(payload: TaskCreatePayload | TaskUpdatePayload, block?: { start_at: string; end_at: string }) {
try {
if (editingTask.value) {
await tasksStore.update(editingTask.value.id, payload as TaskUpdatePayload)
toast.success('Task updated')
} else {
await tasksStore.create(payload as TaskCreatePayload)
const newTask = await tasksStore.create(payload as TaskCreatePayload)
if (block && newTask?.id) {
await tasksStore.createBlock(newTask.id, block)
}
toast.success('Task created')
}
showForm.value = false