PageSourceSearch

https://thinkafrica.fi/_next/static/chunks/2130.034054d47940870b.js

js thinkafrica.fi collected 2026-10-08 12:31:53 UTC 18,607 bytes, 7 lines download raw bytes

1"use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[2130],{12130:(e,r,t)=>{t.r(r),t.d(r,{default:()=>g});var s=t(62384),a=t(35260),l=t(20504),n=t(84485),d=t(99519),i=t(19734),o=t(59875),c=t(13353),u=t(67320),x=t(73273),m=t(20451),p=t(37536);function h(){let e=new Blob([`title,projectSlug,status,referenceCode,stage,keyDeadline,amount,currency,funderId,geographies,thematicAreas,tags
2`],{type:"text/csv;charset=utf-8"}),r=URL.createObjectURL(e),t=document.createElement("a");t.href=r,t.download="grants-import-template.csv",document.body.appendChild(t),t.click(),t.remove(),URL.revokeObjectURL(r)}async function f(e){let r=new FormData;r.append("file",e),r.append("alt","");let t=await (0,p.XX)("/api/media",{method:"POST",body:r,credentials:"include"}),s=await t.json().catch(()=>null);if(!t.ok)throw Error("string"==typeof s?.errors?.[0]?.message?String(s.errors[0].message):"string"==typeof s?.error?String(s.error):`Failed to upload CSV to Media (status ${t.status})`);if(!s||void 0===s?.id)throw Error("Media upload did not return an id");return s}function g(){let[e,r]=a.useState(null),[t,g]=a.useState(""),[j,v]=a.useState("upsert"),[b,N]=a.useState("slug"),[y,w]=a.useState("EUR"),[k,C]=a.useState(!0),[S,R]=a.useState(!1),[I,E]=a.useState("idle"),[U,D]=a.useState(null),[P,$]=a.useState(null),[V,L]=a.useState(null),M="idle"===I&&(!!e||!!t.trim())&&(k||S),J=async()=>{L(null),$(null);try{let r=t.trim();if(e){E("uploading");let t=await f(e);D(t),r=String(t.id)}if(!r)throw Error("Select a CSV file or provide an existing Media ID");E("importing");let s=await (0,p.XX)("/api/admin/projects/grants/import",{method:"POST",headers:{"Content-Type":"application/json"},credentials:"include",body:JSON.stringify({mediaId:r,mode:j,projectKey:b,defaultCurrency:y,dryRun:k})}),a=await s.json().catch(()=>null);if(!s.ok){let e="string"==typeof a?.error?String(a.error):`Import failed (status ${s.status})`;throw Error(e)}$(a)}catch(e){L(e instanceof Error?e.message:"Import failed")}finally{E("idle")}};return(0,s.jsx)("div",{className:"min-h-screen bg-background",children:(0,s.jsxs)("div",{className:"mx-auto flex max-w-4xl flex-col gap-6 px-4 py-8 sm:px-6 lg:px-8",children:[(0,s.jsxs)("header",{children:[(0,s.jsx)(l.A,{backHref:"/admin/collections/grants",label:"← Back to Grants",helpHref:"/docs/projects/grants-import-from-csv",helpLabel:"CSV requirements"}),(0,s.jsxs)("div",{className:"flex flex-col gap-2",children:[(0,s.jsx)("h1",{className:"text-2xl font-semibold md:text-3xl",children:"Import Grants from CSV"}),(0,s.jsx)("p",{className:"text-sm text-muted-foreground",children:"Upload a CSV to Media and run an import into the Grants collection."})]})]}),(0,s.jsxs)(d.Zp,{className:"border-border bg-card",children:[(0,s.jsx)(d.aR,{className:"space-y-1",children:(0,s.jsx)(d.ZB,{className:"text-base",children:"CSV Source"})}),(0,s.jsxs)(d.Wu,{className:"flex flex-col gap-4",children:[(0,s.jsxs)("div",{className:"grid gap-2",children:[(0,s.jsx)(c.J,{htmlFor:"csv",children:"Upload CSV file"}),(0,s.jsx)(o.p,{id:"csv",type:"file",accept:".csv,text/csv,application/vnd.ms-excel",onChange:e=>{r(e.currentTarget.files?.[0]||null),L(null),$(null),D(null)}}),(0,s.jsx)("p",{className:"text-xs text-muted-foreground",children:"The importer reads a CSV stored in the Media collection (max 2MB, max 5,000 rows)."})]}),(0,s.jsxs)("div",{className:"grid gap-2",children:[(0,s.jsx)(c.J,{htmlFor:"mediaId",children:"Or import from existing Media ID"}),(0,s.jsx)(o.p,{id:"mediaId",placeholder:"e.g. 123",value:t,onChange:e=>g(e.target.value)}),U?.id&&(0,s.jsxs)("p",{className:"text-xs text-muted-foreground",children:["Uploaded to Media as ID ",(0,s.jsx)("span",{className:"font-mono",children:String(U.id)}),U.filename?` (${U.filename})`:null]})]}),(0,s.jsxs)("div",{className:"flex flex-wrap gap-3",children:[(0,s.jsx)(n.$,{type:"button",variant:"outline",onClick:h,children:"Download template CSV"}),(0,s.jsx)(n.$,{type:"button",variant:"outline",onClick:()=>window.open("/admin/collections/media","_blank","noreferrer"),children:"Open Media library"})]})]})]}),(0,s.jsxs)(d.Zp,{className:"border-border bg-card",children:[(0,s.jsx)(d.aR,{className:"space-y-1",children:(0,s.jsx)(d.ZB,{className:"text-base",children:"Import Options"})}),(0,s.jsxs)(d.Wu,{className:"grid gap-5",children:[(0,s.jsxs)("div",{className:"grid gap-2",children:[(0,s.jsx)(c.J,{children:"Mode"}),(0,s.jsxs)(x.l6,{value:j,onValueChange:e=>v(e),children:[(0,s.jsx)(x.bq,{children:(0,s.jsx)(x.yv,{placeholder:"Select import mode"})}),(0,s.jsxs)(x.gC,{children:[(0,s.jsx)(x.eb,{value:"upsert",children:"Upsert 
2(update by project + referenceCode)"}),(0,s.jsx)(x.eb,{value:"create-only",children:"Create-only"})]})]}),(0,s.jsx)("p",{className:"text-xs text-muted-foreground",children:"Upsert only updates when a row has a reference code that matches an existing grant in the same project."})]}),(0,s.jsxs)("div",{className:"grid gap-2 sm:grid-cols-2",children:[(0,s.jsxs)("div",{className:"grid gap-2",children:[(0,s.jsx)(c.J,{children:"Project key"}),(0,s.jsxs)(x.l6,{value:b,onValueChange:e=>N(e),children:[(0,s.jsx)(x.bq,{children:(0,s.jsx)(x.yv,{placeholder:"Select project key"})}),(0,s.jsxs)(x.gC,{children:[(0,s.jsx)(x.eb,{value:"slug",children:"projectSlug"}),(0,s.jsx)(x.eb,{value:"id",children:"projectId"})]})]})]}),(0,s.jsxs)("div",{className:"grid gap-2",children:[(0,s.jsx)(c.J,{children:"Default currency"}),(0,s.jsxs)(x.l6,{value:y,onValueChange:e=>w(e),children:[(0,s.jsx)(x.bq,{children:(0,s.jsx)(x.yv,{placeholder:"Select default currency"})}),(0,s.jsxs)(x.gC,{children:[(0,s.jsx)(x.eb,{value:"EUR",children:"EUR"}),(0,s.jsx)(x.eb,{value:"USD",children:"USD"}),(0,s.jsx)(x.eb,{value:"GBP",children:"GBP"})]})]})]})]}),(0,s.jsxs)("div",{className:"flex flex-col gap-2",children:[(0,s.jsxs)("div",{className:"flex items-center gap-2",children:[(0,s.jsx)(i.S,{checked:k,onCheckedChange:e=>C(!!e)}),(0,s.jsx)(c.J,{children:"Dry run (no writes)"})]}),!k&&(0,s.jsxs)("div",{className:"flex items-center gap-2",children:[(0,s.jsx)(i.S,{checked:S,onCheckedChange:e=>R(!!e)}),(0,s.jsx)(c.J,{children:"I understand this will create/update grants"})]})]})]})]}),(0,s.jsxs)("div",{className:"flex items-center justify-end gap-3",children:[(0,s.jsx)(n.$,{type:"button",variant:"outline",disabled:"idle"!==I,onClick:()=>{r(null),g(""),D(null),$(null),L(null),C(!0),R(!1),v("upsert"),N("slug"),w("EUR")},children:"Reset"}),(0,s.jsx)(n.$,{type:"button",disabled:!M,onClick:J,children:"uploading"===I?"Uploading CSV...":"importing"===I?"Running import...":k?"Run dry run":"Run import"})]}),"idle"!==I&&(0,s.jsx)(d.Zp,{className:"border-border bg-card",children:(0,s.jsxs)(d.Wu,{className:"flex flex-col gap-2 pt-6",children:[(0,s.jsx)("div",{className:"text-sm text-muted-foreground",children:"uploading"===I?"Uploading to Media…":"Importing…"}),(0,s.jsx)(u.k,{value:"uploading"===I?35:70})]})}),V&&(0,s.jsxs)(d.Zp,{className:"border-destructive/40 bg-card",children:[(0,s.jsx)(d.aR,{className:"space-y-1",children:(0,s.jsx)(d.ZB,{className:"text-base text-destructive",children:"Import error"})}),(0,s.jsx)(d.Wu,{children:(0,s.jsx)("p",{className:"text-sm text-foreground",children:V})})]}),P&&(0,s.jsxs)(d.Zp,{className:"border-border bg-card",children:[(0,s.jsx)(d.aR,{className:"space-y-1",children:(0,s.jsx)(d.ZB,{className:"text-base",children:"Import Result"})}),(0,s.jsxs)(d.Wu,{className:"flex flex-col gap-4",children:[(0,s.jsxs)("div",{className:"grid gap-2 sm:grid-cols-2",children:[(0,s.jsxs)("div",{className:"rounded-lg border border-border bg-background p-3",children:[(0,s.jsx)("div",{className:"text-xs text-muted-foreground",children:"Success"}),(0,s.jsx)("div",{className:"text-sm font-medium",children:P.success?"Yes":"No"})]}),(0,s.jsxs)("div",{className:"rounded-lg border border-border bg-background p-3",children:[(0,s.jsx)("div",{className:"text-xs text-muted-foreground",children:"Dry run"}),(0,s.jsx)("div",{className:"text-sm font-medium",children:P.dryRun?"Yes":"No"})]}),(0,s.jsxs)("div",{className:"rounded-lg border border-border bg-background p-3",children:[(0,s.jsx)("div",{className:"text-xs text-muted-foreground",children:"Processed"}),(0,s.jsxs)("div",{className:"text-sm font-medium",children:[P.processed," / ",P.totalRows]})]}),(0,s.jsxs)("div",{className:"rounded-lg border border-border bg-background p-3",children:[(0,s.jsx)("div",{className:"text-xs text-muted-foreground",children:"Created / Updated"}),(0,s.jsxs)("div",{className:"text-sm font-medium",children:[P.created," / ",P.updated]})]})]}),P.errors?.length?(0,s.jsxs)("div",{className:"grid gap-2",children:[(0,s.jsx)(c.J,{children:"Row errors"}),(0,s.jsx)(m.T,{readOnly:!0,value:P.errors.map(e=>`Row ${e.row}: ${e.message}`).join("\n"),className:"min-h-[160px] font-mono text-xs"}),(0,s.jsx)("p",{className:"text-xs text-muted-foreground",children:"Fix the CSV and re-run; rows with validation errors are skipped."})]}):(0,s.jsx)("p",{className:"text-sm text-muted-foreground",children:"No row-level validation errors."})]})]})]})})}},19734:(e,r,t)=>{t.d(r,{S:()=>d});var s=t(62384),a=t(12002),l=t(36021),n=t(21017);t(35260);let d=({className:e,ref:r,...t})=>(0,s.jsx)(l.Root,{className:(0,a.cn)("peer h-4 w-4 shrink-0 rounded border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",e),ref:r,...t,children:(0,s.jsx)(l.C1,{className:(0,a.cn)("flex items-center justify-center text-current"),children:(0,s.jsx)(n.A,{className:"h-4 w-4"})})})},20504:(e,r,t)=>{t.d(r,{A:()=>
2o});var s=t(62384),a=t(10691),l=t.n(a);t(35260);var n=t(59123);function d({href:e,children:r,className:t,openInNewTab:a=!0}){let l="/docs"===e||e.startsWith("/docs/")?(0,n.J)(e):e;return(0,s.jsx)("a",{href:l,className:t,target:a?"_blank":void 0,rel:a?"noopener noreferrer":void 0,children:r})}var i=t(12002);function o({backHref:e="/admin",label:r="← Back to Admin",className:t,helpHref:a,helpLabel:n="Need help?"}){return(0,s.jsxs)("div",{className:(0,i.cn)("dashboard-view-header flex items-center justify-between gap-4",t),children:[(0,s.jsx)(l(),{href:e,className:"dashboard-back-link inline-flex items-center gap-2 text-sm text-muted-foreground hover:text-foreground",children:r}),a&&(0,s.jsx)(d,{href:a,className:"inline-flex items-center gap-2 text-xs font-medium text-primary hover:underline",children:n})]})}},36021:(e,r,t)=>{t.d(r,{C1:()=>w,Root:()=>N});var s=t(35260),a=t(20162),l=t(35700),n=t(98226),d=t(5557),i=t(16185),o=t(9185),c=t(79158),u=t(72031),x=t(62384),m="Checkbox",[p,h]=(0,l.A)(m),[f,g]=p(m);function j(e){let{__scopeCheckbox:r,checked:t,children:a,defaultChecked:l,disabled:n,form:i,name:o,onCheckedChange:c,required:u,value:p="on",internal_do_not_use_render:h}=e,[g,j]=(0,d.i)({prop:t,defaultProp:l??!1,onChange:c,caller:m}),[v,b]=s.useState(null),[N,y]=s.useState(null),w=s.useRef(!1),k=!v||!!i||!!v.closest("form"),C={checked:g,disabled:n,setChecked:j,control:v,setControl:b,name:o,form:i,value:p,hasConsumerStoppedPropagationRef:w,required:u,defaultChecked:!S(l)&&l,isFormControl:k,bubbleInput:N,setBubbleInput:y};return(0,x.jsx)(f,{scope:r,...C,children:"function"==typeof h?h(C):a})}var v="CheckboxTrigger",b=s.forwardRef(({__scopeCheckbox:e,onKeyDown:r,onClick:t,...l},d)=>{let{control:i,value:o,disabled:c,checked:m,required:p,setControl:h,setChecked:f,hasConsumerStoppedPropagationRef:j,isFormControl:b,bubbleInput:N}=g(v,e),y=(0,a.s)(d,h),w=s.useRef(m);return s.useEffect(()=>{let e=i?.form;if(e){let r=()=>f(w.current);return e.addEventListener("reset",r),()=>e.removeEventListener("reset",r)}},[i,f]),(0,x.jsx)(u.sG.button,{type:"button",role:"checkbox","aria-checked":S(m)?"mixed":m,"aria-required":p,"data-state":R(m),"data-disabled":c?"":void 0,disabled:c,value:o,...l,ref:y,onKeyDown:(0,n.m)(r,e=>{"Enter"===e.key&&e.preventDefault()}),onClick:(0,n.m)(t,e=>{f(e=>!!S(e)||!e),N&&b&&(j.current=e.isPropagationStopped(),j.current||e.stopPropagation())})})});b.displayName=v;var N=s.forwardRef((e,r)=>{let{__scopeCheckbox:t,name:s,checked:a,defaultChecked:l,required:n,disabled:d,value:i,onCheckedChange:o,form:c,...u}=e;return(0,x.jsx)(j,{__scopeCheckbox:t,checked:a,defaultChecked:l,disabled:d,required:n,onCheckedChange:o,name:s,form:c,value:i,internal_do_not_use_render:({isFormControl:e})=>(0,x.jsxs)(x.Fragment,{children:[(0,x.jsx)(b,{...u,ref:r,__scopeCheckbox:t}),e&&(0,x.jsx)(C,{__scopeCheckbox:t})]})})});N.displayName=m;var y="CheckboxIndicator",w=s.forwardRef((e,r)=>{let{__scopeCheckbox:t,forceMount:s,...a}=e,l=g(y,t);return(0,x.jsx)(c.C,{present:s||S(l.checked)||!0===l.checked,children:(0,x.jsx)(u.sG.span,{"data-state":R(l.checked),"data-disabled":l.disabled?"":void 0,...a,ref:r,style:{pointerEvents:"none",...e.style}})})});w.displayName=y;var k="CheckboxBubbleInput",C=s.forwardRef(({__scopeCheckbox:e,...r},t)=>{let{control:l,hasConsumerStoppedPropagationRef:n,checked:d,defaultChecked:c,required:m,disabled:p,name:h,value:f,form:j,bubbleInput:v,setBubbleInput:b}=g(k,e),N=(0,a.s)(t,b),y=(0,i.Z)(d),w=(0,o.X)(l);
2s.useEffect(()=>{if(!v)return;let e=Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype,"checked").set,r=!n.current;if(y!==d&&e){let t=new Event("click",{bubbles:r});v.indeterminate=S(d),e.call(v,!S(d)&&d),v.dispatchEvent(t)}},[v,y,d,n]);let C=s.useRef(!S(d)&&d);return(0,x.jsx)(u.sG.input,{type:"checkbox","aria-hidden":!0,defaultChecked:c??C.current,required:m,disabled:p,name:h,value:f,form:j,...r,tabIndex:-1,ref:N,style:{...r.style,...w,position:"absolute",pointerEvents:"none",opacity:0,margin:0,transform:"translateX(-100%)"}})});function S(e){return"indeterminate"===e}function R(e){return S(e)?"indeterminate":e?"checked":"unchecked"}C.displayName=k},54235:(e,r,t)=>{t.d(r,{C1:()=>N,bL:()=>b});var s=t(35260),a=t(35700),l=t(72031),n=t(62384),d="Progress",[i,o]=(0,a.A)(d),[c,u]=i(d),x=s.forwardRef((e,r)=>{var t,s;let{__scopeProgress:a,value:d=null,max:i,getValueLabel:o=h,...u}=e;(i||0===i)&&!j(i)&&console.error((t=`${i}`,`Invalid prop \`max\` of value \`${t}\` supplied to \`Progress\`. Only numbers greater than 0 are valid max values. Defaulting to \`100\`.`));let x=j(i)?i:100;null===d||v(d,x)||console.error((s=`${d}`,`Invalid prop \`value\` of value \`${s}\` supplied to \`Progress\`. The \`value\` prop must be:
3  - a positive number
4  - less than the value passed to \`max\` (or 100 if no \`max\` prop is set)
5  - \`null\` or \`undefined\` if the progress is indeterminate.
6
7Defaulting to \`null\`.`));let m=v(d,x)?d:null,p=g(m)?o(m,x):void 0;return(0,n.jsx)(c,{scope:a,value:m,max:x,children:(0,n.jsx)(l.sG.div,{"aria-valuemax":x,"aria-valuemin":0,"aria-valuenow":g(m)?m:void 0,"aria-valuetext":p,role:"progressbar","data-state":f(m,x),"data-value":m??void 0,"data-max":x,...u,ref:r})})});x.displayName=d;var m="ProgressIndicator",p=s.forwardRef((e,r)=>{let{__scopeProgress:t,...s}=e,a=u(m,t);return(0,n.jsx)(l.sG.div,{"data-state":f(a.value,a.max),"data-value":a.value??void 0,"data-max":a.max,...s,ref:r})});function h(e,r){return`${Math.round(e/r*100)}%`}function f(e,r){return null==e?"indeterminate":e===r?"complete":"loading"}function g(e){return"number"==typeof e}function j(e){return g(e)&&!isNaN(e)&&e>0}function v(e,r){return g(e)&&!isNaN(e)&&e<=r&&e>=0}p.displayName=m;var b=x,N=p},59123:(e,r,t)=>{t.d(r,{J:()=>s});function s(e){return`/api/admin/docs-handoff?to=${encodeURIComponent(e)}`}},67320:(e,r,t)=>{t.d(r,{k:()=>d});var s=t(62384),a=t(35260),l=t(54235),n=t(12002);let d=a.forwardRef(({className:e,value:r,...t},a)=>(0,s.jsx)(l.bL,{ref:a,className:(0,n.cn)("relative h-2 w-full overflow-hidden rounded-full bg-muted",e),...t,children:(0,s.jsx)(l.C1,{className:"h-full w-full flex-1 bg-primary transition-all",style:{transform:`translateX(-${100-(r||0)}%)`}})}));d.displayName=l.bL.displayName},73273:(e,r,t)=>{t.d(r,{bq:()=>x,eb:()=>f,gC:()=>h,l6:()=>o,s3:()=>c,yv:()=>u});var s=t(62384),a=t(12002),l=t(22291),n=t(33573),d=t(91144),i=t(21017);t(35260);let o=l.bL,c=l.YJ,u=l.WT,x=({children:e,className:r,ref:t,...d})=>(0,s.jsxs)(l.l9,{"data-slot":"select-trigger",className:(0,a.cn)("flex h-10 w-full items-center justify-between rounded border border-input bg-background px-3 py-2 text-inherit ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",r),ref:t,...d,children:[e,(0,s.jsx)(l.In,{asChild:!0,children:(0,s.jsx)(n.A,{className:"h-4 w-4 opacity-50"})})]}),m=({className:e,ref:r,...t})=>(0,s.jsx)(l.PP,{"data-slot":"select-scroll-up",className:(0,a.cn)("flex cursor-default items-center justify-center py-1",e),ref:r,...t,children:(0,s.jsx)(d.A,{className:"h-4 w-4"})}),p=({className:e,ref:r,...t})=>(0,s.jsx)(l.wn,{"data-slot":"select-scroll-down",className:(0,a.cn)("flex cursor-default items-center justify-center py-1",e),ref:r,...t,children:(0,s.jsx)(n.A,{className:"h-4 w-4"})}),h=({children:e,className:r,position:t="popper",ref:n,...d})=>(0,s.jsx)(l.ZL,{children:(0,s.jsxs)(l.UC,{"data-slot":"select-content",className:(0,a.cn)("relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded border border-border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:sli
7de-in-from-left-2 data-[side=top]:slide-in-from-bottom-2","popper"===t&&"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",r),position:t,ref:n,...d,children:[(0,s.jsx)(m,{}),(0,s.jsx)(l.LM,{className:(0,a.cn)("p-1","popper"===t&&"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"),children:e}),(0,s.jsx)(p,{})]})}),f=({children:e,className:r,ref:t,...n})=>(0,s.jsxs)(l.q7,{"data-slot":"select-item",className:(0,a.cn)("relative flex w-full cursor-default select-none items-center rounded py-1.5 pl-8 pr-2 text-sm outline-none bg-popover text-popover-foreground hover:bg-muted focus:bg-muted data-[disabled]:pointer-events-none data-[disabled]:opacity-50",r),ref:t,...n,children:[(0,s.jsx)("span",{className:"absolute left-2 flex h-3.5 w-3.5 items-center justify-center",children:(0,s.jsx)(l.VF,{children:(0,s.jsx)(i.A,{className:"h-4 w-4"})})}),(0,s.jsx)(l.p4,{children:e})]})}}]);

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.