PageSourceSearch

https://solarpanelcosts.co.uk/assets/Expenses-BD_9Ia8c.js

js solarpanelcosts.co.uk collected 2026-10-10 01:24:36 UTC 7,347 bytes, 1 lines download raw bytes

1import{r as l,j as e,i as G,B as h}from"./index-C44yip9_.js";import{supabase as v}from"./client-BtoXvpDL.js";import{P as L}from"./PageHeader-D_9W3o9h.js";import{I as b}from"./input-CXPidkMd.js";import{L as m}from"./label-DZD-3EqO.js";import{T as M}from"./textarea-COAsPzdL.js";import{S as D,a as w,b as A,c as E,d as y}from"./select-DJMf1kdV.js";import{S as O}from"./skeleton-BFhLtRrQ.js";import{D as R,a as H,b as q,c as z,d as J,e as K}from"./dialog-PdWrTh4Q.js";import{b as Q}from"./charts-CngdNUtA.js";import{u as N}from"./index-BFLbRUn_.js";import{f as U}from"./dateUtils-CJ2h2Dde.js";import{P as W}from"./plus-BxARi54a.js";import{T as X}from"./trash-2-BDsZleMs.js";import"./index-DONtB0qn.js";import"./index-DXfFnIv-.js";const T=["ads","tools","hosting","content","contractors","legal","other"],I=["hsl(var(--lavender))","hsl(var(--lime))","hsl(var(--coral))","hsl(var(--midnight))","hsl(var(--muted-foreground))","#cfa3ff","#9ad9b6"],u=t=>new Intl.NumberFormat("en-GB",{style:"currency",currency:"GBP",maximumFractionDigits:0}).format(t);function pe(){const[t,x]=l.useState(null),[F,g]=l.useState(!1),[r,i]=l.useState({category:"ads",vendor:"",description:"",amount_gbp:"",spent_on:new Date().toISOString().slice(0,10)}),[C,_]=l.useState(!1),[p,k]=l.useState("all"),j=()=>v.from("expenses").select("*").order("spent_on",{ascending:!1}).limit(500).then(({data:s})=>x(s||[]));l.useEffect(()=>{j()},[]);const d=l.useMemo(()=>t==null?void 0:t.filter(s=>p==="all"||s.category===p),[t,p]),o=l.useMemo(()=>{if(!t)return null;const s=t.reduce((a,c)=>a+Number(c.amount_gbp||0),0),n=new Date(Date.now()-30*864e5).toISOString().slice(0,10),V=t.filter(a=>a.spent_on>=n).reduce((a,c)=>a+Number(c.amount_gbp||0),0),f=new Map;for(const a of t)f.set(a.category,(f.get(a.category)||0)+Number(a.amount_gbp||0));return{total:s,last30:V,byCat:Array.from(f.entries()).map(([a,c])=>({name:a,value:c}))}},[t]),B=async()=>{_(!0);const{error:s}=await v.from("expenses").insert({category:r.category,vendor:r.vendor.trim()||null,description:r.description.trim()||null,amount_gbp:Number(r.amount_gbp)||0,spent_on:r.spent_on});if(_(!1),s)return N.error(s.message);N.success("Expense logged"),g(!1),i({category:"ads",vendor:"",description:"",amount_gbp:"",spent_on:new Date().toISOString().slice(0,10)}),j()},P=async s=>{if(!confirm("Delete this expense?"))return;const{error:n}=await v.from("expenses").delete().eq("id",s);if(n)return N.error(n.message);j()};return e.jsxs("div",{className:"space-y-6",children:[e.jsxs(G,{children:[e.jsx("title",{children:"Expenses · Control"}),e.jsx("meta",{name:"robots",content:"noindex, nofollow"})]}),e.jsx(L,{title:"Expenses",subtitle:"Ad spend, tools, and other costs.",actions:e.jsxs(R,{open:F,onOpenChange:g,children:[e.jsx(H,{asChild:!0,children:e.jsxs(h,{children:[e.jsx(W,{className:"h-4 w-4 mr-1.5"}),"Add expense"]})}),e.jsxs(q,{children:[e.jsx(z,{children:e.jsx(J,{children:"Add expense"})}),e.jsxs("div",{className:"space-y-3",children:[e.jsxs("div",{className:"grid grid-cols-2 gap-3",children:[e.jsxs("div",{children:[e.jsx(m,{children:"Category"}),e.jsxs(D,{value:r.category,onValueChange:s=>i({...r,category:s}),children:[e.jsx(w,{children:e.jsx(A,{})}),e.jsx(E,{children:T.map(s=>e.jsx(y,{value:s,children:s},s))})]})]}),e.jsxs("div",{children:[e.jsx(m,{children:"Amount (£)"}),e.jsx(b,{type:"number",inputMode:"decimal",value:r.amount_gbp,onChange:s=>i({...r,amount_gbp:s.target.value})})]})]}),e.jsxs("div",{children:[e.jsx(m,{children:"Vendor"}),e.jsx(b,{value:r.vendor,onChange:s=>i({...r,vendor:s.target.value})})]}),e.jsxs("div",{children:[e.jsx(m,{children:"Description"}),e.jsx(M,{value:r.description,onChange:s=>i({...r,description:s.target.value})})]}),e.jsxs("div",{children:[e.jsx(m,{children:"Date"}),e.jsx(b,{type:"date",value:r.spent_on,onChange:s=>i({...r,spent_on:s.target.value})})]})]}),e.jsxs(K,{children:[e.jsx(h,{variant:"ghost",onClick:()=>g(!1),children:"Cancel"}),e.jsx(h,{onClick:B,disabled:C||!r.amount_gbp,children:C?"Saving…":"Save"})]})]})]})}),e.jsxs("div",{className:"grid grid-cols-2 md:grid-cols-3 gap-4",children:[e.jsx(S,{label:"Total spent",value:o?u(o.total):void 0}),e.jsx(S,{label:"Last 30 days",value:o?u(o.last30):void 0}),e.jsx(S,{label:"Entries",value:t==null?void 0:t.length})]}),e.jsxs("div",{className:"grid lg:grid-cols-2 gap-6",children:[e.jsxs("div",{className:"rounded-2xl border border-border bg-card p-4 sm:p-5",children:[e.jsx("h2",{className:"font-display text-lg mb-3",children:"Spend by category"}),e.jsx("div",{className:"h-64",children:o&&o.byCat.length>0?e.jsx(Q,{data:o.byCat.map((s,n)=>({name:s.name,value:s.value,color:I[n%I.length]})),height:256,valueFormatter:s=>u(s)}):e.jsx("div",{className:"h-full flex items-center justify-center text-sm text-muted-foreground",children:"No expenses yet."})})]}),e.jsxs("div",{className:"rounded-2xl border border-border bg-card p-4 sm:p-5",children:[e.jsx("h2",{className:"font-display text-lg mb-3",children:"Filter"}),e.jsxs(D,{value:p,onValueChange:k,children:[e.jsx(w,{children:e.jsx(A,{})}),e.jsxs(E,{children:[e.jsx(y,{value:"all",children:"All categories"}),T.map(s=>e.jsx(y,{value:s,children:s},s))]})]}),e.jsxs("p",{className:"text-xs text-muted-foreground mt-3",children:["Showing ",(d==null?void 0:d.length)??0," of ",(t==null?void 0:t.length)??0," entries."]})]})]}),e.jsx("div",{className:"rounded-2xl border border-border bg-card p-4 sm:p-5",children:e.jsx("div",{className:"overflow-x-auto",children:e.jsxs("table",{className:"w-full text-sm",children:[e.jsx("thead",{className:"text-left text-xs uppercase tracking-wider text-muted-foreground",children:e.jsxs("tr",{children:[e.jsx("th",{className:"py-2 pr-3",children:"Category"}),e.jsx("th",{className:"py-2 pr-3",children:"Vendor"}),e.jsx("th",{className:"py-2 pr-3",children:"Description"}),e.jsx("th",{className:"py-2 pr-3 text-right",children:"Amount"}),e.jsx("th",{className:"py-2 pr-3",children:"Date"}),e.jsx("th",{className:"py-2"})]})}),e.jsx("tbody",{className:"divide-y divide-border",children:d?d.length===0?e.jsx("tr",{children:e.jsx("td",{colSpan:6,className:"py-8 text-center text-muted-foreground",children:"No expenses logged yet."})}):d.map(s=>e.jsxs("tr",{children:[e.jsx("td",{className:"py-2.5 pr-3",children:e.jsx("span",{className:"rounded-full bg-muted px-2 py-0.5 text-xs",children:s.category})}),e.jsx("td",{className:"py-2.5 pr-3",children:s.vendor||"—"}),e.jsx("td",{className:"py-2.5 pr-3 text-xs text-muted-foreground max-w-xs truncate",children:s.description||"—"}),e.jsx("td",{className:"py-2.5 pr-3 text-right font-medium",children:u(Number(s.amount_gbp))}),e.jsx("td",{className:"py-2.5 pr-3 text-xs text-muted-foreground whitespace-nowrap",children:U(s.spent_on)}),e.jsx("td",{className:"py-2.5 text-right",children:e.jsx(h,{variant:"ghost",size:"sm",onClick:()=>P(s.id),children:e.jsx(X,{className:"h-4 w-4"})})})]},s.id)):Array.from({length:5}).map((s,n)=>e.jsx("tr",{children:e.jsx("td",{colSpan:6,className:"py-3",children:e.jsx(O,{className:"h-5 w-full"})})}
1,n))})]})})})]})}function S({label:t,value:x}){return e.jsxs("div",{className:"rounded-2xl border border-border bg-card p-4",children:[e.jsx("div",{className:"font-display text-2xl",children:x===void 0?e.jsx(O,{className:"h-7 w-16"}):x}),e.jsx("div",{className:"text-xs text-muted-foreground mt-1",children:t})]})}export{pe as default};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.