@epic-web/workshop-app 6.72.0 → 6.72.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/client/assets/{_-dWSrc4Ox.js → _-C4AWl_1u.js} +2 -2
- package/build/client/assets/{_-dWSrc4Ox.js.map → _-C4AWl_1u.js.map} +1 -1
- package/build/client/assets/{_exerciseNumber-FdJ9Amz3.js → _exerciseNumber-DUwXPNgl.js} +2 -2
- package/build/client/assets/{_exerciseNumber-FdJ9Amz3.js.map → _exerciseNumber-DUwXPNgl.js.map} +1 -1
- package/build/client/assets/{_exerciseNumber_._stepNumber.index-WLW-sVZ0.js → _exerciseNumber_._stepNumber.index-BPqMtmN8.js} +2 -2
- package/build/client/assets/{_exerciseNumber_._stepNumber.index-WLW-sVZ0.js.map → _exerciseNumber_._stepNumber.index-BPqMtmN8.js.map} +1 -1
- package/build/client/assets/{_exerciseNumber_.finished-Z7QZ9wk-.js → _exerciseNumber_.finished-C4sVkuEq.js} +2 -2
- package/build/client/assets/{_exerciseNumber_.finished-Z7QZ9wk-.js.map → _exerciseNumber_.finished-C4sVkuEq.js.map} +1 -1
- package/build/client/assets/{_extra-UkIdJCI9.js → _extra-DkiDFYVP.js} +2 -2
- package/build/client/assets/{_extra-UkIdJCI9.js.map → _extra-DkiDFYVP.js.map} +1 -1
- package/build/client/assets/{_layout-CjWZzKD8.js → _layout-C9vSX5Ti.js} +2 -2
- package/build/client/assets/{_layout-CjWZzKD8.js.map → _layout-C9vSX5Ti.js.map} +1 -1
- package/build/client/assets/{_layout-DQM0capK.js → _layout-DDTXkx2z.js} +2 -2
- package/build/client/assets/{_layout-DQM0capK.js.map → _layout-DDTXkx2z.js.map} +1 -1
- package/build/client/assets/{entry.client-DSlySyW6.js → entry.client-DdbTov6T.js} +3 -3
- package/build/client/assets/{entry.client-DSlySyW6.js.map → entry.client-DdbTov6T.js.map} +1 -1
- package/build/client/assets/{error-boundary-DVp6wKiz.js → error-boundary-DxHqAEHX.js} +3 -3
- package/build/client/assets/{error-boundary-DVp6wKiz.js.map → error-boundary-DxHqAEHX.js.map} +1 -1
- package/build/client/assets/{finished-PFz2gTpr.js → finished-BW_4ow3n.js} +2 -2
- package/build/client/assets/{finished-PFz2gTpr.js.map → finished-BW_4ow3n.js.map} +1 -1
- package/build/client/assets/{index-DkDXAMak.js → index-BgExztV_.js} +2 -2
- package/build/client/assets/{index-DkDXAMak.js.map → index-BgExztV_.js.map} +1 -1
- package/build/client/assets/{index-DuFMeXSm.js → index-DroM8fzS.js} +2 -2
- package/build/client/assets/{index-DuFMeXSm.js.map → index-DroM8fzS.js.map} +1 -1
- package/build/client/assets/{index-CATk0s1i.js → index-IgBUhsxX.js} +2 -2
- package/build/client/assets/{index-CATk0s1i.js.map → index-IgBUhsxX.js.map} +1 -1
- package/build/client/assets/{index-DfT8l3cx.js → index-PKjGT1Rp.js} +2 -2
- package/build/client/assets/{index-DfT8l3cx.js.map → index-PKjGT1Rp.js.map} +1 -1
- package/build/client/assets/index.client-CsIUPfXc.js +500 -0
- package/build/client/assets/index.client-CsIUPfXc.js.map +1 -0
- package/build/client/assets/{manifest-6451f320.js → manifest-9e0c0428.js} +1 -1
- package/build/client/assets/progress-BcA4i0iU.js +2 -0
- package/build/client/assets/progress-BcA4i0iU.js.map +1 -0
- package/build/client/assets/{root-CB01jtcy.js → root-CKcj2hDc.js} +3 -3
- package/build/client/assets/{root-CB01jtcy.js.map → root-CKcj2hDc.js.map} +1 -1
- package/build/server/index.js +16 -2
- package/build/server/index.js.map +1 -1
- package/package.json +9 -9
- package/build/client/assets/index.client-B5fbXzJV.js +0 -500
- package/build/client/assets/index.client-B5fbXzJV.js.map +0 -1
- package/build/client/assets/progress-ILaVQtOO.js +0 -2
- package/build/client/assets/progress-ILaVQtOO.js.map +0 -1
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/index.client-
|
|
2
|
-
import{_ as p}from"./preload-helper-BXl3LOEh.js";import{j as e}from"./jsx-runtime-C5WNSv3b.js";import{r as n}from"./index-CqIc3cxq.js";import{h as x,i as j,j as i}from"./chunk-EPOLDU6W-BCLmut3y.js";import{g as m}from"./misc-W4055b-0.js";const f=await p(()=>import("./index.client-
|
|
3
|
-
//# sourceMappingURL=error-boundary-
|
|
1
|
+
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/index.client-CsIUPfXc.js","assets/index-CqIc3cxq.js"])))=>i.map(i=>d[i]);
|
|
2
|
+
import{_ as p}from"./preload-helper-BXl3LOEh.js";import{j as e}from"./jsx-runtime-C5WNSv3b.js";import{r as n}from"./index-CqIc3cxq.js";import{h as x,i as j,j as i}from"./chunk-EPOLDU6W-BCLmut3y.js";import{g as m}from"./misc-W4055b-0.js";const f=await p(()=>import("./index.client-CsIUPfXc.js"),__vite__mapDeps([0,1])).catch(s=>(console.warn("Failed to import @sentry/react-router:",s instanceof Error?s.message:String(s),"- Sentry monitoring will be disabled but the application will continue to work normally"),null)),v={400:({error:s})=>e.jsxs("div",{children:[e.jsx("h1",{children:"Bad Request"}),e.jsx("p",{children:s.data})]}),401:()=>e.jsxs("div",{children:[e.jsx("h1",{children:"Unauthorized"}),e.jsx("p",{children:"You don't have permission to access this resource."})]}),403:()=>e.jsxs("div",{children:[e.jsx("h1",{children:"Forbidden"}),e.jsx("p",{children:"You don't have permission to access this resource."})]}),404:()=>e.jsxs("div",{children:[e.jsx("h1",{children:"Not Found"}),e.jsx("p",{children:"Sorry, we couldn't find what you were looking for."})]}),500:({error:s})=>e.jsxs("div",{children:[e.jsx("h1",{children:"Internal Server Error"}),e.jsx("p",{children:"Sorry, something went wrong on our end."}),e.jsx("p",{children:s.data})]}),502:()=>e.jsxs("div",{children:[e.jsx("h1",{children:"Bad Gateway"}),e.jsx("p",{children:"Sorry, we're having a temporary problem. Please try again later."}),e.jsx("button",{onClick:()=>window.location.reload(),children:"Refresh"})]}),503:()=>e.jsxs("div",{children:[e.jsx("h1",{children:"Service Unavailable"}),e.jsx("p",{children:"Sorry, we're having a temporary problem. Please try again later."}),e.jsx("button",{onClick:()=>window.location.reload(),children:"Refresh"})]})};function P({className:s="container flex items-center justify-center p-20 text-h2 flex-col gap-6",defaultStatusHandler:a=({error:t})=>e.jsxs("p",{children:[t.status," ",t.data]}),statusHandlers:l,unexpectedErrorHandler:c=t=>e.jsx("p",{children:m(t)})}){const[t,d]=n.useState(!1),r=x(),h=j(),o=i(r),u={...v,...l};return n.useEffect(()=>{o||ENV.EPICSHOP_IS_PUBLISHED&&f?.captureException(r)},[r,o]),n.useEffect(()=>{r instanceof Error&&r.message.includes("Failed to fetch")&&fetch("/resources/healthcheck").catch(()=>{d(!0)})},[r]),typeof document<"u"&&console.error(r),e.jsxs("div",{className:s,children:[t?e.jsxs("div",{className:"flex flex-col items-center justify-center gap-6",children:[e.jsx("h1",{className:"text-h2 font-bold",children:"Server is down"}),e.jsx("p",{className:"text-body-md",children:ENV.EPICSHOP_DEPLOYED?"Sorry, we're having a temporary problem. Please try again later.":"Your server is not running. Please restart the workshop app."})]}):null,i(r)?(u?.[r.status]??a)({error:r,params:h}):c(r)]})}export{P as G};
|
|
3
|
+
//# sourceMappingURL=error-boundary-DxHqAEHX.js.map
|
package/build/client/assets/{error-boundary-DVp6wKiz.js.map → error-boundary-DxHqAEHX.js.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"mappings":";6OACA,MAAMA,EAAS,MAAAC,EAAA,IAAM,OAAO,4BAAsB,0BAAE,MAAOC,IAC1D,QAAQ,KACP,yCACAA,aAAiB,MAAQA,EAAM,QAAU,OAAOA,CAAK,EACrD,2FAEM,KACP,EAgBKC,EAAuD,CAC5D,IAAK,CAAC,CAAE,MAAAD,CAAA,WACN,OACA,UAAAE,MAAC,MAAG,uBAAW,EACfA,MAAC,KAAG,SAAAF,EAAM,KAAK,GAChB,EAED,IAAK,IACJG,OAAC,OACA,UAAAD,MAAC,MAAG,wBAAY,EAChBA,MAAC,KAAE,8DAAkD,GACtD,EAED,IAAK,IACJC,OAAC,OACA,UAAAD,MAAC,MAAG,qBAAS,EACbA,MAAC,KAAE,8DAAkD,GACtD,EAED,IAAK,IACJC,OAAC,OACA,UAAAD,MAAC,MAAG,qBAAS,EACbA,MAAC,KAAE,8DAAkD,GACtD,EAED,IAAK,CAAC,CAAE,MAAAF,CAAA,WACN,OACA,UAAAE,MAAC,MAAG,iCAAqB,EACzBA,MAAC,KAAE,mDAAuC,EAC1CA,MAAC,KAAG,SAAAF,EAAM,KAAK,GAChB,EAED,IAAK,IACJG,OAAC,OACA,UAAAD,MAAC,MAAG,uBAAW,EACfA,MAAC,KAAE,4EAAgE,EACnEA,MAAC,UAAO,QAAS,IAAM,OAAO,SAAS,SAAU,mBAAO,GACzD,EAED,IAAK,IACJC,OAAC,OACA,UAAAD,MAAC,MAAG,+BAAmB,EACvBA,MAAC,KAAE,4EAAgE,EACnEA,MAAC,UAAO,QAAS,IAAM,OAAO,SAAS,SAAU,mBAAO,GACzD,CAEF,EAEO,SAASE,EAAqB,CACpC,UAAAC,EAAY,yEACZ,qBAAAC,EAAuB,CAAC,CAAE,MAAAN,CAAA,WACxB,KACC,UAAAA,EAAM,OAAO,IAAEA,EAAM,MACvB,EAED,eAAgBO,EAChB,uBAAAC,EAA0BR,SAAW,KAAG,SAAAS,EAAgBT,CAAK,EAAE,CAChE,EAKG,CACF,KAAM,CAACU,EAAcC,CAAe,EAAIC,WAAS,EAAK,EAChDZ,EAAQa,EAAA,EACRC,EAASC,EAAA,EACTC,EAAaC,EAAqBjB,CAAK,EACvCkB,EAAiB,CACtB,GAAGjB,EACH,GAAGM,CAAA,EAGJY,mBAAU,IAAM,CACXH,GACA,IAAI,uBACPlB,GAAQ,iBAAiBE,CAAK,CAEhC,EAAG,CAACA,EAAOgB,CAAU,CAAC,EAEtBG,YAAU,IAAM,CAEXnB,aAAiB,OAASA,EAAM,QAAQ,SAAS,iBAAiB,GACrE,MAAM,wBAAwB,EAAE,MAAM,IAAM,CAC3CW,EAAgB,EAAI,CACrB,CAAC,CAEH,EAAG,CAACX,CAAK,CAAC,EAEN,OAAO,SAAa,KACvB,QAAQ,MAAMA,CAAK,EAInBG,OAAC,OAAI,UAAAE,EACH,UAAAK,EACAP,OAAC,OAAI,UAAU,kDACd,UAAAD,MAAC,MAAG,UAAU,oBAAoB,0BAAc,QAC/C,KAAE,UAAU,eACX,aAAI,kBACF,mEACA,+DACJ,GACD,EACG,KACHe,EAAqBjB,CAAK,GACvBkB,IAAiBlB,EAAM,MAAM,GAAKM,GAAsB,CACzD,MAAAN,EACA,OAAAc,CAAA,CACA,EACAN,EAAuBR,CAAK,GAChC,CAEF","names":["Sentry","__vitePreload","error","defaultStatusHandlers","jsx","jsxs","GeneralErrorBoundary","className","defaultStatusHandler","givenStatusHandlers","unexpectedErrorHandler","getErrorMessage","isServerDown","setIsServerDown","useState","useRouteError","params","useParams","isResponse","isRouteErrorResponse","statusHandlers","useEffect"],"ignoreList":[],"sources":["../../../app/components/error-boundary.tsx"],"sourcesContent":["// Dynamic import of Sentry with error handling\nconst Sentry = await import('@sentry/react-router').catch((error) => {\n\tconsole.warn(\n\t\t'Failed to import @sentry/react-router:',\n\t\terror instanceof Error ? error.message : String(error),\n\t\t'- Sentry monitoring will be disabled but the application will continue to work normally',\n\t)\n\treturn null\n})\n\nimport { useEffect, useState } from 'react'\nimport {\n\tisRouteErrorResponse,\n\tuseParams,\n\tuseRouteError,\n\ttype ErrorResponse,\n} from 'react-router'\nimport { getErrorMessage } from '#app/utils/misc.tsx'\n\ntype StatusHandler = (info: {\n\terror: ErrorResponse\n\tparams: Record<string, string | undefined>\n}) => React.ReactNode | null\n\nconst defaultStatusHandlers: Record<number, StatusHandler> = {\n\t400: ({ error }) => (\n\t\t<div>\n\t\t\t<h1>Bad Request</h1>\n\t\t\t<p>{error.data}</p>\n\t\t</div>\n\t),\n\t401: () => (\n\t\t<div>\n\t\t\t<h1>Unauthorized</h1>\n\t\t\t<p>You don't have permission to access this resource.</p>\n\t\t</div>\n\t),\n\t403: () => (\n\t\t<div>\n\t\t\t<h1>Forbidden</h1>\n\t\t\t<p>You don't have permission to access this resource.</p>\n\t\t</div>\n\t),\n\t404: () => (\n\t\t<div>\n\t\t\t<h1>Not Found</h1>\n\t\t\t<p>Sorry, we couldn't find what you were looking for.</p>\n\t\t</div>\n\t),\n\t500: ({ error }) => (\n\t\t<div>\n\t\t\t<h1>Internal Server Error</h1>\n\t\t\t<p>Sorry, something went wrong on our end.</p>\n\t\t\t<p>{error.data}</p>\n\t\t</div>\n\t),\n\t502: () => (\n\t\t<div>\n\t\t\t<h1>Bad Gateway</h1>\n\t\t\t<p>Sorry, we're having a temporary problem. Please try again later.</p>\n\t\t\t<button onClick={() => window.location.reload()}>Refresh</button>\n\t\t</div>\n\t),\n\t503: () => (\n\t\t<div>\n\t\t\t<h1>Service Unavailable</h1>\n\t\t\t<p>Sorry, we're having a temporary problem. Please try again later.</p>\n\t\t\t<button onClick={() => window.location.reload()}>Refresh</button>\n\t\t</div>\n\t),\n}\n\nexport function GeneralErrorBoundary({\n\tclassName = 'container flex items-center justify-center p-20 text-h2 flex-col gap-6',\n\tdefaultStatusHandler = ({ error }) => (\n\t\t<p>\n\t\t\t{error.status} {error.data}\n\t\t</p>\n\t),\n\tstatusHandlers: givenStatusHandlers,\n\tunexpectedErrorHandler = (error) => <p>{getErrorMessage(error)}</p>,\n}: {\n\tclassName?: string\n\tdefaultStatusHandler?: StatusHandler\n\tstatusHandlers?: Record<number, StatusHandler>\n\tunexpectedErrorHandler?: (error: unknown) => React.ReactNode | null\n}) {\n\tconst [isServerDown, setIsServerDown] = useState(false)\n\tconst error = useRouteError()\n\tconst params = useParams()\n\tconst isResponse = isRouteErrorResponse(error)\n\tconst statusHandlers = {\n\t\t...defaultStatusHandlers,\n\t\t...givenStatusHandlers,\n\t}\n\n\tuseEffect(() => {\n\t\tif (isResponse) return\n\t\tif (ENV.EPICSHOP_IS_PUBLISHED) {\n\t\t\tSentry?.captureException(error)\n\t\t}\n\t}, [error, isResponse])\n\n\tuseEffect(() => {\n\t\t// if error is \"Failed to fetch\", trigger a fetch to '/resources/healthcheck' and if it fails, show a message to the user that the server is down\n\t\tif (error instanceof Error && error.message.includes('Failed to fetch')) {\n\t\t\tfetch('/resources/healthcheck').catch(() => {\n\t\t\t\tsetIsServerDown(true)\n\t\t\t})\n\t\t}\n\t}, [error])\n\n\tif (typeof document !== 'undefined') {\n\t\tconsole.error(error)\n\t}\n\n\treturn (\n\t\t<div className={className}>\n\t\t\t{isServerDown ? (\n\t\t\t\t<div className=\"flex flex-col items-center justify-center gap-6\">\n\t\t\t\t\t<h1 className=\"text-h2 font-bold\">Server is down</h1>\n\t\t\t\t\t<p className=\"text-body-md\">\n\t\t\t\t\t\t{ENV.EPICSHOP_DEPLOYED\n\t\t\t\t\t\t\t? \"Sorry, we're having a temporary problem. Please try again later.\"\n\t\t\t\t\t\t\t: 'Your server is not running. Please restart the workshop app.'}\n\t\t\t\t\t</p>\n\t\t\t\t</div>\n\t\t\t) : null}\n\t\t\t{isRouteErrorResponse(error)\n\t\t\t\t? (statusHandlers?.[error.status] ?? defaultStatusHandler)({\n\t\t\t\t\t\terror,\n\t\t\t\t\t\tparams,\n\t\t\t\t\t})\n\t\t\t\t: unexpectedErrorHandler(error)}\n\t\t</div>\n\t)\n}\n"],"file":"error-boundary-
|
|
1
|
+
{"version":3,"mappings":";6OACA,MAAMA,EAAS,MAAAC,EAAA,IAAM,OAAO,4BAAsB,0BAAE,MAAOC,IAC1D,QAAQ,KACP,yCACAA,aAAiB,MAAQA,EAAM,QAAU,OAAOA,CAAK,EACrD,2FAEM,KACP,EAgBKC,EAAuD,CAC5D,IAAK,CAAC,CAAE,MAAAD,CAAA,WACN,OACA,UAAAE,MAAC,MAAG,uBAAW,EACfA,MAAC,KAAG,SAAAF,EAAM,KAAK,GAChB,EAED,IAAK,IACJG,OAAC,OACA,UAAAD,MAAC,MAAG,wBAAY,EAChBA,MAAC,KAAE,8DAAkD,GACtD,EAED,IAAK,IACJC,OAAC,OACA,UAAAD,MAAC,MAAG,qBAAS,EACbA,MAAC,KAAE,8DAAkD,GACtD,EAED,IAAK,IACJC,OAAC,OACA,UAAAD,MAAC,MAAG,qBAAS,EACbA,MAAC,KAAE,8DAAkD,GACtD,EAED,IAAK,CAAC,CAAE,MAAAF,CAAA,WACN,OACA,UAAAE,MAAC,MAAG,iCAAqB,EACzBA,MAAC,KAAE,mDAAuC,EAC1CA,MAAC,KAAG,SAAAF,EAAM,KAAK,GAChB,EAED,IAAK,IACJG,OAAC,OACA,UAAAD,MAAC,MAAG,uBAAW,EACfA,MAAC,KAAE,4EAAgE,EACnEA,MAAC,UAAO,QAAS,IAAM,OAAO,SAAS,SAAU,mBAAO,GACzD,EAED,IAAK,IACJC,OAAC,OACA,UAAAD,MAAC,MAAG,+BAAmB,EACvBA,MAAC,KAAE,4EAAgE,EACnEA,MAAC,UAAO,QAAS,IAAM,OAAO,SAAS,SAAU,mBAAO,GACzD,CAEF,EAEO,SAASE,EAAqB,CACpC,UAAAC,EAAY,yEACZ,qBAAAC,EAAuB,CAAC,CAAE,MAAAN,CAAA,WACxB,KACC,UAAAA,EAAM,OAAO,IAAEA,EAAM,MACvB,EAED,eAAgBO,EAChB,uBAAAC,EAA0BR,SAAW,KAAG,SAAAS,EAAgBT,CAAK,EAAE,CAChE,EAKG,CACF,KAAM,CAACU,EAAcC,CAAe,EAAIC,WAAS,EAAK,EAChDZ,EAAQa,EAAA,EACRC,EAASC,EAAA,EACTC,EAAaC,EAAqBjB,CAAK,EACvCkB,EAAiB,CACtB,GAAGjB,EACH,GAAGM,CAAA,EAGJY,mBAAU,IAAM,CACXH,GACA,IAAI,uBACPlB,GAAQ,iBAAiBE,CAAK,CAEhC,EAAG,CAACA,EAAOgB,CAAU,CAAC,EAEtBG,YAAU,IAAM,CAEXnB,aAAiB,OAASA,EAAM,QAAQ,SAAS,iBAAiB,GACrE,MAAM,wBAAwB,EAAE,MAAM,IAAM,CAC3CW,EAAgB,EAAI,CACrB,CAAC,CAEH,EAAG,CAACX,CAAK,CAAC,EAEN,OAAO,SAAa,KACvB,QAAQ,MAAMA,CAAK,EAInBG,OAAC,OAAI,UAAAE,EACH,UAAAK,EACAP,OAAC,OAAI,UAAU,kDACd,UAAAD,MAAC,MAAG,UAAU,oBAAoB,0BAAc,QAC/C,KAAE,UAAU,eACX,aAAI,kBACF,mEACA,+DACJ,GACD,EACG,KACHe,EAAqBjB,CAAK,GACvBkB,IAAiBlB,EAAM,MAAM,GAAKM,GAAsB,CACzD,MAAAN,EACA,OAAAc,CAAA,CACA,EACAN,EAAuBR,CAAK,GAChC,CAEF","names":["Sentry","__vitePreload","error","defaultStatusHandlers","jsx","jsxs","GeneralErrorBoundary","className","defaultStatusHandler","givenStatusHandlers","unexpectedErrorHandler","getErrorMessage","isServerDown","setIsServerDown","useState","useRouteError","params","useParams","isResponse","isRouteErrorResponse","statusHandlers","useEffect"],"ignoreList":[],"sources":["../../../app/components/error-boundary.tsx"],"sourcesContent":["// Dynamic import of Sentry with error handling\nconst Sentry = await import('@sentry/react-router').catch((error) => {\n\tconsole.warn(\n\t\t'Failed to import @sentry/react-router:',\n\t\terror instanceof Error ? error.message : String(error),\n\t\t'- Sentry monitoring will be disabled but the application will continue to work normally',\n\t)\n\treturn null\n})\n\nimport { useEffect, useState } from 'react'\nimport {\n\tisRouteErrorResponse,\n\tuseParams,\n\tuseRouteError,\n\ttype ErrorResponse,\n} from 'react-router'\nimport { getErrorMessage } from '#app/utils/misc.tsx'\n\ntype StatusHandler = (info: {\n\terror: ErrorResponse\n\tparams: Record<string, string | undefined>\n}) => React.ReactNode | null\n\nconst defaultStatusHandlers: Record<number, StatusHandler> = {\n\t400: ({ error }) => (\n\t\t<div>\n\t\t\t<h1>Bad Request</h1>\n\t\t\t<p>{error.data}</p>\n\t\t</div>\n\t),\n\t401: () => (\n\t\t<div>\n\t\t\t<h1>Unauthorized</h1>\n\t\t\t<p>You don't have permission to access this resource.</p>\n\t\t</div>\n\t),\n\t403: () => (\n\t\t<div>\n\t\t\t<h1>Forbidden</h1>\n\t\t\t<p>You don't have permission to access this resource.</p>\n\t\t</div>\n\t),\n\t404: () => (\n\t\t<div>\n\t\t\t<h1>Not Found</h1>\n\t\t\t<p>Sorry, we couldn't find what you were looking for.</p>\n\t\t</div>\n\t),\n\t500: ({ error }) => (\n\t\t<div>\n\t\t\t<h1>Internal Server Error</h1>\n\t\t\t<p>Sorry, something went wrong on our end.</p>\n\t\t\t<p>{error.data}</p>\n\t\t</div>\n\t),\n\t502: () => (\n\t\t<div>\n\t\t\t<h1>Bad Gateway</h1>\n\t\t\t<p>Sorry, we're having a temporary problem. Please try again later.</p>\n\t\t\t<button onClick={() => window.location.reload()}>Refresh</button>\n\t\t</div>\n\t),\n\t503: () => (\n\t\t<div>\n\t\t\t<h1>Service Unavailable</h1>\n\t\t\t<p>Sorry, we're having a temporary problem. Please try again later.</p>\n\t\t\t<button onClick={() => window.location.reload()}>Refresh</button>\n\t\t</div>\n\t),\n}\n\nexport function GeneralErrorBoundary({\n\tclassName = 'container flex items-center justify-center p-20 text-h2 flex-col gap-6',\n\tdefaultStatusHandler = ({ error }) => (\n\t\t<p>\n\t\t\t{error.status} {error.data}\n\t\t</p>\n\t),\n\tstatusHandlers: givenStatusHandlers,\n\tunexpectedErrorHandler = (error) => <p>{getErrorMessage(error)}</p>,\n}: {\n\tclassName?: string\n\tdefaultStatusHandler?: StatusHandler\n\tstatusHandlers?: Record<number, StatusHandler>\n\tunexpectedErrorHandler?: (error: unknown) => React.ReactNode | null\n}) {\n\tconst [isServerDown, setIsServerDown] = useState(false)\n\tconst error = useRouteError()\n\tconst params = useParams()\n\tconst isResponse = isRouteErrorResponse(error)\n\tconst statusHandlers = {\n\t\t...defaultStatusHandlers,\n\t\t...givenStatusHandlers,\n\t}\n\n\tuseEffect(() => {\n\t\tif (isResponse) return\n\t\tif (ENV.EPICSHOP_IS_PUBLISHED) {\n\t\t\tSentry?.captureException(error)\n\t\t}\n\t}, [error, isResponse])\n\n\tuseEffect(() => {\n\t\t// if error is \"Failed to fetch\", trigger a fetch to '/resources/healthcheck' and if it fails, show a message to the user that the server is down\n\t\tif (error instanceof Error && error.message.includes('Failed to fetch')) {\n\t\t\tfetch('/resources/healthcheck').catch(() => {\n\t\t\t\tsetIsServerDown(true)\n\t\t\t})\n\t\t}\n\t}, [error])\n\n\tif (typeof document !== 'undefined') {\n\t\tconsole.error(error)\n\t}\n\n\treturn (\n\t\t<div className={className}>\n\t\t\t{isServerDown ? (\n\t\t\t\t<div className=\"flex flex-col items-center justify-center gap-6\">\n\t\t\t\t\t<h1 className=\"text-h2 font-bold\">Server is down</h1>\n\t\t\t\t\t<p className=\"text-body-md\">\n\t\t\t\t\t\t{ENV.EPICSHOP_DEPLOYED\n\t\t\t\t\t\t\t? \"Sorry, we're having a temporary problem. Please try again later.\"\n\t\t\t\t\t\t\t: 'Your server is not running. Please restart the workshop app.'}\n\t\t\t\t\t</p>\n\t\t\t\t</div>\n\t\t\t) : null}\n\t\t\t{isRouteErrorResponse(error)\n\t\t\t\t? (statusHandlers?.[error.status] ?? defaultStatusHandler)({\n\t\t\t\t\t\terror,\n\t\t\t\t\t\tparams,\n\t\t\t\t\t})\n\t\t\t\t: unexpectedErrorHandler(error)}\n\t\t</div>\n\t)\n}\n"],"file":"error-boundary-DxHqAEHX.js"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{w as n,L as l}from"./chunk-EPOLDU6W-BCLmut3y.js";import{j as e}from"./jsx-runtime-C5WNSv3b.js";import{E as a}from"./index-CEVyDj51.js";import{r as c}from"./index-CqIc3cxq.js";import{E as m}from"./epic-video-DnXXh6qE.js";import{I as d,c as p}from"./misc-W4055b-0.js";import{L as f}from"./loading-CDNzW5oO.js";import{N as h}from"./nav-chevrons-Dk4GtZwQ.js";import{u as x}from"./revalidation-ws-BJWJviUX.js";import{M as u}from"./mdx-Dun3ONG_.js";import{u as j}from"./online-DiNLkgTC.js";import{g as b}from"./root-loader-BOzEMapJ.js";import{g}from"./seo-t5J-DRxw.js";import{E as v}from"./launch-editor-D2exGfVu.js";import{P as w}from"./progress-
|
|
2
|
-
//# sourceMappingURL=finished-
|
|
1
|
+
import{w as n,L as l}from"./chunk-EPOLDU6W-BCLmut3y.js";import{j as e}from"./jsx-runtime-C5WNSv3b.js";import{E as a}from"./index-CEVyDj51.js";import{r as c}from"./index-CqIc3cxq.js";import{E as m}from"./epic-video-DnXXh6qE.js";import{I as d,c as p}from"./misc-W4055b-0.js";import{L as f}from"./loading-CDNzW5oO.js";import{N as h}from"./nav-chevrons-Dk4GtZwQ.js";import{u as x}from"./revalidation-ws-BJWJviUX.js";import{M as u}from"./mdx-Dun3ONG_.js";import{u as j}from"./online-DiNLkgTC.js";import{g as b}from"./root-loader-BOzEMapJ.js";import{g}from"./seo-t5J-DRxw.js";import{E as v}from"./launch-editor-D2exGfVu.js";import{P as w}from"./progress-BcA4i0iU.js";import{u as N}from"./index-CdzVFL-Z.js";import"./use-event-source-BuD4_2SF.js";import"./index-DzdDahau.js";import"./index-vDCSPjrM.js";import"./schemas-Uj5SZtvt.js";import"./tooltip-Tlsyx2YO.js";import"./user-BsPobzjB.js";import"./workshop-config-Zfc8zU0x.js";import"./preload-helper-BXl3LOEh.js";import"./progress-bar-DpWhcyhC.js";import"./pe-CIZUOJMr.js";const Z={getSitemapEntries:()=>[{route:"/finished"}]},_=({matches:r})=>{const s=b(r);return s?g({title:`🎉 ${s?.workshopTitle}`,description:`Elaboration for ${s?.workshopTitle}`,ogTitle:`Finished ${s?.workshopTitle}`,ogDescription:"You finished! Time to submit feedback.",instructor:s.instructor,requestInfo:s.requestInfo}):[]},k={h1:()=>null},ee=n(function({loaderData:s}){return x({watchPaths:["./exercises/FINISHED.mdx"]}),e.jsx("div",{className:"flex h-full grow flex-col",children:e.jsxs("main",{className:"grid h-full grow grid-cols-1 grid-rows-2 lg:grid-cols-2 lg:grid-rows-1",children:[e.jsxs("div",{className:"relative col-span-1 row-span-1 flex h-full flex-col lg:border-r",children:[e.jsx("h1",{className:"h-14 border-b pr-5 pl-10 text-sm leading-none font-medium uppercase",children:e.jsx("div",{className:"flex h-14 flex-wrap items-center justify-between gap-x-2 py-2",children:e.jsxs("div",{className:"flex items-center justify-start gap-x-2",children:[e.jsx(l,{to:"/",className:"hover:underline",children:s.workshopTitle}),e.jsx("span",{children:"/"}),e.jsx("span",{children:"Elaboration"})]})})}),e.jsx("article",{className:"shadow-on-scrollbox scrollbar-thin scrollbar-thumb-scrollbar h-full w-full max-w-none flex-1 scroll-pt-6 space-y-6 overflow-y-auto p-2 sm:p-10 sm:pt-8",id:s.articleId,children:s.finishedCode?e.jsx(m,{epicVideoInfosPromise:s.epicVideoInfosPromise,children:e.jsx("div",{className:"prose dark:prose-invert sm:prose-lg",children:e.jsx(u,{code:s.finishedCode,components:k})})}):"No finished instructions yet..."}),e.jsx(a,{elementQuery:`#${s.articleId}`}),e.jsx(w,{type:"workshop-finished",className:"h-14 border-t px-6"}),e.jsxs("div",{className:"@container flex h-16 justify-between border-t border-b-4 lg:border-b-0",children:[e.jsx("div",{}),s.workshopFinished.status==="success"?e.jsx(v,{file:s.workshopFinished.file,relativePath:s.workshopFinished.relativePath}):null,e.jsx(h,{prev:s.prevStepLink,next:{to:"/"}})]})]}),e.jsx(E,{workshopTitle:s.workshopTitle,workshopFormEmbedUrl:s.workshopFormEmbedUrl})]})})});function E({workshopTitle:r,workshopFormEmbedUrl:s}){const t=N(),[o,i]=c.useState(!1);return j()?e.jsxs("div",{className:"relative shrink-0",children:[o?null:e.jsx("div",{className:"absolute inset-0 z-10 flex items-center justify-center",children:e.jsx(f,{children:e.jsxs("span",{children:["Loading ",r," Elaboration form"]})})}),e.jsx("iframe",{onLoad:()=>i(!0),onError:()=>i(!0),title:"Elaboration",src:s,className:p("absolute inset-0 flex h-full w-full transition-opacity duration-300",o?"opacity-100":"opacity-0"),style:{colorScheme:t}})]}):e.jsx("div",{className:"relative shrink-0",children:e.jsx("div",{className:"text-foreground-destructive absolute inset-0 z-10 flex items-center justify-center",children:e.jsx(d,{name:"WifiNoConnection",size:"xl",children:e.jsxs("span",{children:["Unable to load the ",e.jsx("a",{href:s,className:"underline",children:`${r} feedback form`})," when offline"]})})})})}export{ee as default,Z as handle,_ as meta};
|
|
2
|
+
//# sourceMappingURL=finished-BW_4ow3n.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"finished-PFz2gTpr.js","sources":["../../../app/routes/_app+/finished.tsx"],"sourcesContent":["import { ElementScrollRestoration } from '@epic-web/restore-scroll'\nimport {\n\tgetExercises,\n\tgetWorkshopFinished,\n} from '@epic-web/workshop-utils/apps.server'\nimport { getWorkshopConfig } from '@epic-web/workshop-utils/config.server'\nimport { getEpicVideoInfos } from '@epic-web/workshop-utils/epic-api.server'\nimport {\n\tcombineServerTimings,\n\tgetServerTimeHeader,\n\tmakeTimings,\n\ttime,\n} from '@epic-web/workshop-utils/timing.server'\nimport { type SEOHandle } from '@nasa-gcn/remix-seo'\nimport slugify from '@sindresorhus/slugify'\nimport * as React from 'react'\nimport { data, type HeadersFunction, Link } from 'react-router'\nimport { EpicVideoInfoProvider } from '#app/components/epic-video.tsx'\nimport { Icon } from '#app/components/icons.tsx'\nimport { Loading } from '#app/components/loading.tsx'\nimport { NavChevrons } from '#app/components/nav-chevrons.tsx'\nimport { useRevalidationWS } from '#app/components/revalidation-ws.tsx'\nimport { Mdx } from '#app/utils/mdx.tsx'\nimport { cn } from '#app/utils/misc.tsx'\nimport { useIsOnline } from '#app/utils/online.ts'\nimport { getRootMatchLoaderData } from '#app/utils/root-loader.ts'\nimport { getSeoMetaTags } from '#app/utils/seo.ts'\nimport { EditFileOnGitHub } from '../launch-editor.tsx'\nimport { ProgressToggle } from '../progress.tsx'\nimport { useTheme } from '../theme/index.tsx'\nimport { type Route } from './+types/finished.tsx'\n\nexport const handle: SEOHandle = {\n\tgetSitemapEntries: () => [{ route: '/finished' }],\n}\n\nexport const meta: Route.MetaFunction = ({ matches }) => {\n\tconst rootData = getRootMatchLoaderData(matches)\n\tif (!rootData) return []\n\n\treturn getSeoMetaTags({\n\t\ttitle: `🎉 ${rootData?.workshopTitle}`,\n\t\tdescription: `Elaboration for ${rootData?.workshopTitle}`,\n\t\togTitle: `Finished ${rootData?.workshopTitle}`,\n\t\togDescription: `You finished! Time to submit feedback.`,\n\t\tinstructor: rootData.instructor,\n\t\trequestInfo: rootData.requestInfo,\n\t})\n}\n\nexport async function loader({ request }: Route.LoaderArgs) {\n\tconst timings = makeTimings('finishedLoader')\n\tconst exercises = await getExercises({ request, timings })\n\tconst compiledFinished = await time(() => getWorkshopFinished({ request }), {\n\t\ttimings,\n\t\ttype: 'compileMdx',\n\t\tdesc: 'compileMdx in finished',\n\t})\n\n\tconst lastExercises = exercises[exercises.length - 1]\n\tconst workshopConfig = getWorkshopConfig()\n\tconst workshopTitle = workshopConfig.title\n\tconst workshopFormTemplate = workshopConfig.forms.workshop\n\tconst workshopFormEmbedUrl = workshopFormTemplate.replace(\n\t\t'{workshopTitle}',\n\t\tencodeURIComponent(workshopTitle),\n\t)\n\treturn data(\n\t\t{\n\t\t\tarticleId: `workshop-${slugify(workshopTitle)}-finished`,\n\t\t\tworkshopTitle,\n\t\t\tworkshopFormEmbedUrl,\n\t\t\tfinishedCode:\n\t\t\t\tcompiledFinished.compiled.status === 'success'\n\t\t\t\t\t? compiledFinished.compiled.code\n\t\t\t\t\t: null,\n\t\t\tepicVideoInfosPromise:\n\t\t\t\tcompiledFinished.compiled.status === 'success'\n\t\t\t\t\t? getEpicVideoInfos(compiledFinished.compiled.epicVideoEmbeds, {\n\t\t\t\t\t\t\trequest,\n\t\t\t\t\t\t})\n\t\t\t\t\t: null,\n\t\t\tworkshopFinished: {\n\t\t\t\tstatus: compiledFinished.compiled.status,\n\t\t\t\tfile: compiledFinished.file,\n\t\t\t\trelativePath: compiledFinished.relativePath,\n\t\t\t},\n\t\t\tprevStepLink: lastExercises\n\t\t\t\t? {\n\t\t\t\t\t\tto: `/${lastExercises.exerciseNumber}/finished`,\n\t\t\t\t\t}\n\t\t\t\t: null,\n\t\t},\n\t\t{\n\t\t\theaders: {\n\t\t\t\t'Server-Timing': getServerTimeHeader(timings),\n\t\t\t},\n\t\t},\n\t)\n}\n\nexport const headers: HeadersFunction = ({ loaderHeaders, parentHeaders }) => {\n\tconst headers = {\n\t\t'Cache-Control': loaderHeaders.get('Cache-Control') ?? '',\n\t\t'Server-Timing': combineServerTimings(loaderHeaders, parentHeaders),\n\t}\n\treturn headers\n}\n\nconst mdxComponents = { h1: () => null }\n\nexport default function ExerciseFinished({\n\tloaderData: data,\n}: Route.ComponentProps) {\n\tuseRevalidationWS({ watchPaths: ['./exercises/FINISHED.mdx'] })\n\treturn (\n\t\t<div className=\"flex h-full grow flex-col\">\n\t\t\t<main className=\"grid h-full grow grid-cols-1 grid-rows-2 lg:grid-cols-2 lg:grid-rows-1\">\n\t\t\t\t<div className=\"relative col-span-1 row-span-1 flex h-full flex-col lg:border-r\">\n\t\t\t\t\t<h1 className=\"h-14 border-b pr-5 pl-10 text-sm leading-none font-medium uppercase\">\n\t\t\t\t\t\t<div className=\"flex h-14 flex-wrap items-center justify-between gap-x-2 py-2\">\n\t\t\t\t\t\t\t<div className=\"flex items-center justify-start gap-x-2\">\n\t\t\t\t\t\t\t\t<Link to=\"/\" className=\"hover:underline\">\n\t\t\t\t\t\t\t\t\t{data.workshopTitle}\n\t\t\t\t\t\t\t\t</Link>\n\t\t\t\t\t\t\t\t<span>/</span>\n\t\t\t\t\t\t\t\t<span>Elaboration</span>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</h1>\n\t\t\t\t\t<article\n\t\t\t\t\t\tclassName=\"shadow-on-scrollbox scrollbar-thin scrollbar-thumb-scrollbar h-full w-full max-w-none flex-1 scroll-pt-6 space-y-6 overflow-y-auto p-2 sm:p-10 sm:pt-8\"\n\t\t\t\t\t\tid={data.articleId}\n\t\t\t\t\t>\n\t\t\t\t\t\t{data.finishedCode ? (\n\t\t\t\t\t\t\t<EpicVideoInfoProvider\n\t\t\t\t\t\t\t\tepicVideoInfosPromise={data.epicVideoInfosPromise}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<div className=\"prose dark:prose-invert sm:prose-lg\">\n\t\t\t\t\t\t\t\t\t<Mdx code={data.finishedCode} components={mdxComponents} />\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</EpicVideoInfoProvider>\n\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t// TODO: render a random dad joke...\n\t\t\t\t\t\t\t'No finished instructions yet...'\n\t\t\t\t\t\t)}\n\t\t\t\t\t</article>\n\t\t\t\t\t<ElementScrollRestoration elementQuery={`#${data.articleId}`} />\n\t\t\t\t\t<ProgressToggle\n\t\t\t\t\t\ttype=\"workshop-finished\"\n\t\t\t\t\t\tclassName=\"h-14 border-t px-6\"\n\t\t\t\t\t/>\n\t\t\t\t\t<div className=\"@container flex h-16 justify-between border-t border-b-4 lg:border-b-0\">\n\t\t\t\t\t\t<div />\n\t\t\t\t\t\t{data.workshopFinished.status === 'success' ? (\n\t\t\t\t\t\t\t<EditFileOnGitHub\n\t\t\t\t\t\t\t\tfile={data.workshopFinished.file}\n\t\t\t\t\t\t\t\trelativePath={data.workshopFinished.relativePath}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t<NavChevrons prev={data.prevStepLink} next={{ to: '/' }} />\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t<Survey\n\t\t\t\t\tworkshopTitle={data.workshopTitle}\n\t\t\t\t\tworkshopFormEmbedUrl={data.workshopFormEmbedUrl}\n\t\t\t\t/>\n\t\t\t</main>\n\t\t</div>\n\t)\n}\n\nfunction Survey({\n\tworkshopTitle,\n\tworkshopFormEmbedUrl,\n}: {\n\tworkshopTitle: string\n\tworkshopFormEmbedUrl: string\n}) {\n\tconst theme = useTheme()\n\tconst [iframeLoaded, setIframeLoaded] = React.useState(false)\n\tconst isOnline = useIsOnline()\n\tif (!isOnline) {\n\t\treturn (\n\t\t\t<div className=\"relative shrink-0\">\n\t\t\t\t<div className=\"text-foreground-destructive absolute inset-0 z-10 flex items-center justify-center\">\n\t\t\t\t\t<Icon name=\"WifiNoConnection\" size=\"xl\">\n\t\t\t\t\t\t<span>\n\t\t\t\t\t\t\t{'Unable to load the '}\n\t\t\t\t\t\t\t<a href={workshopFormEmbedUrl} className=\"underline\">\n\t\t\t\t\t\t\t\t{`${workshopTitle} feedback form`}\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t{' when offline'}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</Icon>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t)\n\t}\n\treturn (\n\t\t<div className=\"relative shrink-0\">\n\t\t\t{!iframeLoaded ? (\n\t\t\t\t<div className=\"absolute inset-0 z-10 flex items-center justify-center\">\n\t\t\t\t\t<Loading>\n\t\t\t\t\t\t<span>Loading {workshopTitle} Elaboration form</span>\n\t\t\t\t\t</Loading>\n\t\t\t\t</div>\n\t\t\t) : null}\n\t\t\t<iframe\n\t\t\t\tonLoad={() => setIframeLoaded(true)}\n\t\t\t\t// show what would have shown if there is an error\n\t\t\t\tonError={() => setIframeLoaded(true)}\n\t\t\t\ttitle=\"Elaboration\"\n\t\t\t\tsrc={workshopFormEmbedUrl}\n\t\t\t\tclassName={cn(\n\t\t\t\t\t'absolute inset-0 flex h-full w-full transition-opacity duration-300',\n\t\t\t\t\tiframeLoaded ? 'opacity-100' : 'opacity-0',\n\t\t\t\t)}\n\t\t\t\tstyle={{ colorScheme: theme }}\n\t\t\t/>\n\t\t</div>\n\t)\n}\n"],"names":["handle","getSitemapEntries","route","meta","matches","rootData","getRootMatchLoaderData","getSeoMetaTags","title","workshopTitle","description","ogTitle","ogDescription","instructor","requestInfo","mdxComponents","h1","finished","_UNSAFE_withComponentProps","loaderData","data","useRevalidationWS","watchPaths","className","children","jsxs","jsx","Link","to","id","articleId","finishedCode","EpicVideoInfoProvider","epicVideoInfosPromise","Mdx","code","components","ElementScrollRestoration","elementQuery","ProgressToggle","type","workshopFinished","status","EditFileOnGitHub","file","relativePath","NavChevrons","prev","prevStepLink","next","Survey","workshopFormEmbedUrl","theme","useTheme","iframeLoaded","setIframeLoaded","React","useIsOnline","Loading","onLoad","onError","src","cn","style","colorScheme","Icon","name","size","href"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"finished-BW_4ow3n.js","sources":["../../../app/routes/_app+/finished.tsx"],"sourcesContent":["import { ElementScrollRestoration } from '@epic-web/restore-scroll'\nimport {\n\tgetExercises,\n\tgetWorkshopFinished,\n} from '@epic-web/workshop-utils/apps.server'\nimport { getWorkshopConfig } from '@epic-web/workshop-utils/config.server'\nimport { getEpicVideoInfos } from '@epic-web/workshop-utils/epic-api.server'\nimport {\n\tcombineServerTimings,\n\tgetServerTimeHeader,\n\tmakeTimings,\n\ttime,\n} from '@epic-web/workshop-utils/timing.server'\nimport { type SEOHandle } from '@nasa-gcn/remix-seo'\nimport slugify from '@sindresorhus/slugify'\nimport * as React from 'react'\nimport { data, type HeadersFunction, Link } from 'react-router'\nimport { EpicVideoInfoProvider } from '#app/components/epic-video.tsx'\nimport { Icon } from '#app/components/icons.tsx'\nimport { Loading } from '#app/components/loading.tsx'\nimport { NavChevrons } from '#app/components/nav-chevrons.tsx'\nimport { useRevalidationWS } from '#app/components/revalidation-ws.tsx'\nimport { Mdx } from '#app/utils/mdx.tsx'\nimport { cn } from '#app/utils/misc.tsx'\nimport { useIsOnline } from '#app/utils/online.ts'\nimport { getRootMatchLoaderData } from '#app/utils/root-loader.ts'\nimport { getSeoMetaTags } from '#app/utils/seo.ts'\nimport { EditFileOnGitHub } from '../launch-editor.tsx'\nimport { ProgressToggle } from '../progress.tsx'\nimport { useTheme } from '../theme/index.tsx'\nimport { type Route } from './+types/finished.tsx'\n\nexport const handle: SEOHandle = {\n\tgetSitemapEntries: () => [{ route: '/finished' }],\n}\n\nexport const meta: Route.MetaFunction = ({ matches }) => {\n\tconst rootData = getRootMatchLoaderData(matches)\n\tif (!rootData) return []\n\n\treturn getSeoMetaTags({\n\t\ttitle: `🎉 ${rootData?.workshopTitle}`,\n\t\tdescription: `Elaboration for ${rootData?.workshopTitle}`,\n\t\togTitle: `Finished ${rootData?.workshopTitle}`,\n\t\togDescription: `You finished! Time to submit feedback.`,\n\t\tinstructor: rootData.instructor,\n\t\trequestInfo: rootData.requestInfo,\n\t})\n}\n\nexport async function loader({ request }: Route.LoaderArgs) {\n\tconst timings = makeTimings('finishedLoader')\n\tconst exercises = await getExercises({ request, timings })\n\tconst compiledFinished = await time(() => getWorkshopFinished({ request }), {\n\t\ttimings,\n\t\ttype: 'compileMdx',\n\t\tdesc: 'compileMdx in finished',\n\t})\n\n\tconst lastExercises = exercises[exercises.length - 1]\n\tconst workshopConfig = getWorkshopConfig()\n\tconst workshopTitle = workshopConfig.title\n\tconst workshopFormTemplate = workshopConfig.forms.workshop\n\tconst workshopFormEmbedUrl = workshopFormTemplate.replace(\n\t\t'{workshopTitle}',\n\t\tencodeURIComponent(workshopTitle),\n\t)\n\treturn data(\n\t\t{\n\t\t\tarticleId: `workshop-${slugify(workshopTitle)}-finished`,\n\t\t\tworkshopTitle,\n\t\t\tworkshopFormEmbedUrl,\n\t\t\tfinishedCode:\n\t\t\t\tcompiledFinished.compiled.status === 'success'\n\t\t\t\t\t? compiledFinished.compiled.code\n\t\t\t\t\t: null,\n\t\t\tepicVideoInfosPromise:\n\t\t\t\tcompiledFinished.compiled.status === 'success'\n\t\t\t\t\t? getEpicVideoInfos(compiledFinished.compiled.epicVideoEmbeds, {\n\t\t\t\t\t\t\trequest,\n\t\t\t\t\t\t})\n\t\t\t\t\t: null,\n\t\t\tworkshopFinished: {\n\t\t\t\tstatus: compiledFinished.compiled.status,\n\t\t\t\tfile: compiledFinished.file,\n\t\t\t\trelativePath: compiledFinished.relativePath,\n\t\t\t},\n\t\t\tprevStepLink: lastExercises\n\t\t\t\t? {\n\t\t\t\t\t\tto: `/${lastExercises.exerciseNumber}/finished`,\n\t\t\t\t\t}\n\t\t\t\t: null,\n\t\t},\n\t\t{\n\t\t\theaders: {\n\t\t\t\t'Server-Timing': getServerTimeHeader(timings),\n\t\t\t},\n\t\t},\n\t)\n}\n\nexport const headers: HeadersFunction = ({ loaderHeaders, parentHeaders }) => {\n\tconst headers = {\n\t\t'Cache-Control': loaderHeaders.get('Cache-Control') ?? '',\n\t\t'Server-Timing': combineServerTimings(loaderHeaders, parentHeaders),\n\t}\n\treturn headers\n}\n\nconst mdxComponents = { h1: () => null }\n\nexport default function ExerciseFinished({\n\tloaderData: data,\n}: Route.ComponentProps) {\n\tuseRevalidationWS({ watchPaths: ['./exercises/FINISHED.mdx'] })\n\treturn (\n\t\t<div className=\"flex h-full grow flex-col\">\n\t\t\t<main className=\"grid h-full grow grid-cols-1 grid-rows-2 lg:grid-cols-2 lg:grid-rows-1\">\n\t\t\t\t<div className=\"relative col-span-1 row-span-1 flex h-full flex-col lg:border-r\">\n\t\t\t\t\t<h1 className=\"h-14 border-b pr-5 pl-10 text-sm leading-none font-medium uppercase\">\n\t\t\t\t\t\t<div className=\"flex h-14 flex-wrap items-center justify-between gap-x-2 py-2\">\n\t\t\t\t\t\t\t<div className=\"flex items-center justify-start gap-x-2\">\n\t\t\t\t\t\t\t\t<Link to=\"/\" className=\"hover:underline\">\n\t\t\t\t\t\t\t\t\t{data.workshopTitle}\n\t\t\t\t\t\t\t\t</Link>\n\t\t\t\t\t\t\t\t<span>/</span>\n\t\t\t\t\t\t\t\t<span>Elaboration</span>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</h1>\n\t\t\t\t\t<article\n\t\t\t\t\t\tclassName=\"shadow-on-scrollbox scrollbar-thin scrollbar-thumb-scrollbar h-full w-full max-w-none flex-1 scroll-pt-6 space-y-6 overflow-y-auto p-2 sm:p-10 sm:pt-8\"\n\t\t\t\t\t\tid={data.articleId}\n\t\t\t\t\t>\n\t\t\t\t\t\t{data.finishedCode ? (\n\t\t\t\t\t\t\t<EpicVideoInfoProvider\n\t\t\t\t\t\t\t\tepicVideoInfosPromise={data.epicVideoInfosPromise}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<div className=\"prose dark:prose-invert sm:prose-lg\">\n\t\t\t\t\t\t\t\t\t<Mdx code={data.finishedCode} components={mdxComponents} />\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</EpicVideoInfoProvider>\n\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t// TODO: render a random dad joke...\n\t\t\t\t\t\t\t'No finished instructions yet...'\n\t\t\t\t\t\t)}\n\t\t\t\t\t</article>\n\t\t\t\t\t<ElementScrollRestoration elementQuery={`#${data.articleId}`} />\n\t\t\t\t\t<ProgressToggle\n\t\t\t\t\t\ttype=\"workshop-finished\"\n\t\t\t\t\t\tclassName=\"h-14 border-t px-6\"\n\t\t\t\t\t/>\n\t\t\t\t\t<div className=\"@container flex h-16 justify-between border-t border-b-4 lg:border-b-0\">\n\t\t\t\t\t\t<div />\n\t\t\t\t\t\t{data.workshopFinished.status === 'success' ? (\n\t\t\t\t\t\t\t<EditFileOnGitHub\n\t\t\t\t\t\t\t\tfile={data.workshopFinished.file}\n\t\t\t\t\t\t\t\trelativePath={data.workshopFinished.relativePath}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t<NavChevrons prev={data.prevStepLink} next={{ to: '/' }} />\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t<Survey\n\t\t\t\t\tworkshopTitle={data.workshopTitle}\n\t\t\t\t\tworkshopFormEmbedUrl={data.workshopFormEmbedUrl}\n\t\t\t\t/>\n\t\t\t</main>\n\t\t</div>\n\t)\n}\n\nfunction Survey({\n\tworkshopTitle,\n\tworkshopFormEmbedUrl,\n}: {\n\tworkshopTitle: string\n\tworkshopFormEmbedUrl: string\n}) {\n\tconst theme = useTheme()\n\tconst [iframeLoaded, setIframeLoaded] = React.useState(false)\n\tconst isOnline = useIsOnline()\n\tif (!isOnline) {\n\t\treturn (\n\t\t\t<div className=\"relative shrink-0\">\n\t\t\t\t<div className=\"text-foreground-destructive absolute inset-0 z-10 flex items-center justify-center\">\n\t\t\t\t\t<Icon name=\"WifiNoConnection\" size=\"xl\">\n\t\t\t\t\t\t<span>\n\t\t\t\t\t\t\t{'Unable to load the '}\n\t\t\t\t\t\t\t<a href={workshopFormEmbedUrl} className=\"underline\">\n\t\t\t\t\t\t\t\t{`${workshopTitle} feedback form`}\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t{' when offline'}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</Icon>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t)\n\t}\n\treturn (\n\t\t<div className=\"relative shrink-0\">\n\t\t\t{!iframeLoaded ? (\n\t\t\t\t<div className=\"absolute inset-0 z-10 flex items-center justify-center\">\n\t\t\t\t\t<Loading>\n\t\t\t\t\t\t<span>Loading {workshopTitle} Elaboration form</span>\n\t\t\t\t\t</Loading>\n\t\t\t\t</div>\n\t\t\t) : null}\n\t\t\t<iframe\n\t\t\t\tonLoad={() => setIframeLoaded(true)}\n\t\t\t\t// show what would have shown if there is an error\n\t\t\t\tonError={() => setIframeLoaded(true)}\n\t\t\t\ttitle=\"Elaboration\"\n\t\t\t\tsrc={workshopFormEmbedUrl}\n\t\t\t\tclassName={cn(\n\t\t\t\t\t'absolute inset-0 flex h-full w-full transition-opacity duration-300',\n\t\t\t\t\tiframeLoaded ? 'opacity-100' : 'opacity-0',\n\t\t\t\t)}\n\t\t\t\tstyle={{ colorScheme: theme }}\n\t\t\t/>\n\t\t</div>\n\t)\n}\n"],"names":["handle","getSitemapEntries","route","meta","matches","rootData","getRootMatchLoaderData","getSeoMetaTags","title","workshopTitle","description","ogTitle","ogDescription","instructor","requestInfo","mdxComponents","h1","finished","_UNSAFE_withComponentProps","loaderData","data","useRevalidationWS","watchPaths","className","children","jsxs","jsx","Link","to","id","articleId","finishedCode","EpicVideoInfoProvider","epicVideoInfosPromise","Mdx","code","components","ElementScrollRestoration","elementQuery","ProgressToggle","type","workshopFinished","status","EditFileOnGitHub","file","relativePath","NavChevrons","prev","prevStepLink","next","Survey","workshopFormEmbedUrl","theme","useTheme","iframeLoaded","setIframeLoaded","React","useIsOnline","Loading","onLoad","onError","src","cn","style","colorScheme","Icon","name","size","href"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{w as j,a as E,c as A,d as C,e as N}from"./chunk-EPOLDU6W-BCLmut3y.js";import{j as r}from"./jsx-runtime-C5WNSv3b.js";import{g as T,R as H,P as U,C as t,a as b,D as B}from"./playground-DmEAkxG1.js";import{U as I}from"./diff-HIgoC1PO.js";import{G as R}from"./error-boundary-
|
|
2
|
-
//# sourceMappingURL=index-
|
|
1
|
+
import{w as j,a as E,c as A,d as C,e as N}from"./chunk-EPOLDU6W-BCLmut3y.js";import{j as r}from"./jsx-runtime-C5WNSv3b.js";import{g as T,R as H,P as U,C as t,a as b,D as B}from"./playground-DmEAkxG1.js";import{U as I}from"./diff-HIgoC1PO.js";import{G as R}from"./error-boundary-DxHqAEHX.js";import{u as S}from"./workshop-config-Zfc8zU0x.js";import{u as O}from"./misc-W4055b-0.js";import{P as a}from"./preview-fhmjENlm.js";import{T as k}from"./tests-1-kVRtTc.js";import"./index-CqIc3cxq.js";import"./loading-CDNzW5oO.js";import"./index-CdzVFL-Z.js";import"./tooltip-Tlsyx2YO.js";import"./index-vDCSPjrM.js";import"./root-loader-BOzEMapJ.js";import"./pe-CIZUOJMr.js";import"./schemas-Uj5SZtvt.js";import"./discord-BJkw0IrB.js";import"./user-BsPobzjB.js";import"./online-DiNLkgTC.js";import"./index-CJDOQ1bl.js";import"./index-ynYvVAOK.js";import"./status-indicator-C6DiLYL5.js";import"./index-DzdDahau.js";import"./set-playground-BSGwH9dH.js";import"./button-Cd-ekki5.js";import"./onboarding-indicator-B-XR90_G.js";import"./progress-bar-DpWhcyhC.js";import"./dialog-CzO65Z5w.js";import"./playground-window-x2mQ5o1O.js";import"./accordion-CQ7oujC6.js";import"./mdx-Dun3ONG_.js";import"./epic-video-DnXXh6qE.js";import"./use-event-source-BuD4_2SF.js";import"./preload-helper-BXl3LOEh.js";import"./launch-editor-D2exGfVu.js";import"./revalidation-ws-BJWJviUX.js";const l=["playground","problem","solution","tests","diff","chat"],L=n=>!!(n&&l.includes(n)),Ps=j(function({loaderData:e}){const{inBrowserBrowserRef:p}=A(),d=S(),[u]=C(),f=u.get("preview"),h=O(),x=N();function m(s){if(s==="tests")return ENV.EPICSHOP_DEPLOYED||!e.playground||e.playground.test.type==="none";if(s==="problem"||s==="solution"){if(e[s]?.dev.type==="none")return!0;if(ENV.EPICSHOP_DEPLOYED){const i=e[s]?.dev.type;return i!=="browser"&&i!=="export"&&!e[s]?.stackBlitzUrl}}return s==="playground"&&ENV.EPICSHOP_DEPLOYED?!0:s==="chat"?!d.product.discordChannelId:!1}function w(s){if(s==="tests"){if(!e.playground)return null;const{isTestRunning:i,testExitCode:o}=e.playground;return i?"running":o===0?"passed":o!==null&&o!==0?"failed":null}return(s==="problem"||s==="solution"||s==="playground")&&(s==="playground"?e.playground:e[s])?.isRunning?"running":null}const y=L(f)?f:l.find(s=>!m(s)),c=`/diff?${new URLSearchParams({app1:e.problem?.name??"",app2:e.solution?.name??""})}`;function g(s){s.altKey&&!s.ctrlKey&&!s.shiftKey&&!s.metaKey&&(s.preventDefault(),x(c))}const v=l.map(s=>{const i=m(s),o=w(s),P=s==="diff"&&h?c:`?${T(u,s,"playground")}`;return{id:s,label:s,hidden:i,status:o,to:P,onClick:s==="diff"?g:void 0}});return r.jsxs(H,{className:"relative flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden sm:col-span-1 sm:row-span-1",value:y,children:[r.jsx(U,{tabs:v}),r.jsxs("div",{className:"relative z-10 flex min-h-0 flex-1 flex-col overflow-hidden",children:[r.jsx(t,{value:"playground",className:"radix-state-inactive:hidden flex min-h-0 w-full grow basis-0 items-stretch justify-center self-start",forceMount:!0,children:r.jsx(b,{appInfo:e.playground,problemAppName:e.problem?.name,inBrowserBrowserRef:p,allApps:e.allApps,isUpToDate:e.playground?.isUpToDate??!1})}),r.jsx(t,{value:"problem",className:"radix-state-inactive:hidden flex min-h-0 w-full grow basis-0 items-stretch justify-center self-start",forceMount:!0,children:r.jsx(a,{appInfo:e.problem,inBrowserBrowserRef:p})}),r.jsx(t,{value:"solution",className:"radix-state-inactive:hidden flex min-h-0 w-full grow basis-0 items-stretch justify-center self-start",forceMount:!0,children:r.jsx(a,{appInfo:e.solution,inBrowserBrowserRef:p})}),r.jsx(t,{value:"tests",className:"radix-state-inactive:hidden flex min-h-0 w-full grow basis-0 items-stretch justify-center self-start overflow-hidden",children:r.jsx(k,{appInfo:e.playground,problemAppName:e.problem?.name,allApps:e.allApps,isUpToDate:e.playground?.isUpToDate??!1,userHasAccessPromise:e.userHasAccessPromise})}),r.jsx(t,{value:"diff",className:"radix-state-inactive:hidden flex h-full min-h-0 w-full grow basis-0 items-stretch justify-center self-start",children:r.jsx(I,{diff:e.diff,allApps:e.allApps,userHasAccessPromise:e.userHasAccessPromise})}),r.jsx(t,{value:"chat",className:"radix-state-inactive:hidden flex h-full min-h-0 w-full grow basis-0 items-stretch justify-center self-start",children:r.jsx(B,{discordPostsPromise:e.discordPostsPromise})})]})]})}),js=E(function(){return r.jsx(R,{statusHandlers:{404:()=>r.jsx("p",{children:"Sorry, we couldn't find an app here."})}})});export{js as ErrorBoundary,Ps as default};
|
|
2
|
+
//# sourceMappingURL=index-BgExztV_.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index-DkDXAMak.js","sources":["../../../app/routes/_app+/exercise+/$exerciseNumber_.$stepNumber.$type+/index.tsx"],"sourcesContent":["import {\n\tgetAppByName,\n\tgetAppDisplayName,\n\tgetApps,\n\tgetExerciseApp,\n\tisExerciseStepApp,\n\tisPlaygroundApp,\n\trequireExerciseApp,\n\ttype App,\n\ttype ExerciseStepApp,\n} from '@epic-web/workshop-utils/apps.server'\nimport { getDiffCode } from '@epic-web/workshop-utils/diff.server'\nimport { userHasAccessToExerciseStep } from '@epic-web/workshop-utils/epic-api.server'\nimport {\n\tcombineServerTimings,\n\tgetServerTimeHeader,\n\tmakeTimings,\n} from '@epic-web/workshop-utils/timing.server'\nimport * as Tabs from '@radix-ui/react-tabs'\nimport * as React from 'react'\nimport {\n\tuseNavigate,\n\tuseSearchParams,\n\tdata,\n\tredirect,\n\ttype HeadersFunction,\n\ttype LoaderFunctionArgs,\n\tuseOutletContext,\n} from 'react-router'\nimport { Diff } from '#app/components/diff.tsx'\nimport { DiscordChat } from '#app/components/discord-chat.tsx'\nimport { GeneralErrorBoundary } from '#app/components/error-boundary.tsx'\nimport { type InBrowserBrowserRef } from '#app/components/in-browser-browser.tsx'\nimport {\n\tgetPreviewSearchParams,\n\tPreviewTabsList,\n} from '#app/components/preview-tabs.tsx'\nimport { useWorkshopConfig } from '#app/components/workshop-config.tsx'\nimport { fetchDiscordPosts } from '#app/utils/discord.server.ts'\nimport { useAltDown } from '#app/utils/misc.tsx'\nimport { type Route } from './+types/index.ts'\nimport { Playground } from './__shared/playground.tsx'\nimport { Preview } from './__shared/preview.tsx'\nimport { Tests } from './__shared/tests.tsx'\nimport { getAppRunningState, getTestState } from './__shared/utils.tsx'\n\nexport async function loader({ request, params }: LoaderFunctionArgs) {\n\tconst timings = makeTimings('exerciseStepTypeIndexLoader')\n\tconst searchParams = new URL(request.url).searchParams\n\tconst cacheOptions = { request, timings }\n\n\tconst [exerciseStepApp, allAppsFull, problemApp, solutionApp] =\n\t\tawait Promise.all([\n\t\t\trequireExerciseApp(params, cacheOptions),\n\t\t\tgetApps(cacheOptions),\n\t\t\tgetExerciseApp({ ...params, type: 'problem' }, cacheOptions),\n\t\t\tgetExerciseApp({ ...params, type: 'solution' }, cacheOptions),\n\t\t])\n\n\tconst playgroundApp = allAppsFull.find(isPlaygroundApp)\n\tconst reqUrl = new URL(request.url)\n\n\tconst pathnameParam = reqUrl.searchParams.get('pathname')\n\tif (pathnameParam === '' || pathnameParam === '/') {\n\t\treqUrl.searchParams.delete('pathname')\n\t\tthrow redirect(reqUrl.toString())\n\t}\n\n\tconst app1Name = reqUrl.searchParams.get('app1')\n\tconst app2Name = reqUrl.searchParams.get('app2')\n\tconst app1 = app1Name\n\t\t? await getAppByName(app1Name)\n\t\t: playgroundApp || problemApp\n\tconst app2 = app2Name ? await getAppByName(app2Name) : solutionApp\n\n\tfunction getStepId(a: ExerciseStepApp) {\n\t\treturn (\n\t\t\ta.exerciseNumber * 1000 +\n\t\t\ta.stepNumber * 10 +\n\t\t\t(a.type === 'problem' ? 0 : 1)\n\t\t)\n\t}\n\n\tfunction getStepNameAndId(a: App) {\n\t\tif (isExerciseStepApp(a)) {\n\t\t\tconst exerciseNumberStr = String(a.exerciseNumber).padStart(2, '0')\n\t\t\tconst stepNumberStr = String(a.stepNumber).padStart(2, '0')\n\n\t\t\treturn {\n\t\t\t\tstepName: `${exerciseNumberStr}/${stepNumberStr}.${a.type}`,\n\t\t\t\tstepId: getStepId(a),\n\t\t\t}\n\t\t}\n\t\treturn { stepName: '', stepId: -1 }\n\t}\n\n\tconst allApps = allAppsFull\n\t\t.filter((a, i, ar) => ar.findIndex((b) => a.name === b.name) === i)\n\t\t.map((a) => ({\n\t\t\tdisplayName: getAppDisplayName(a, allAppsFull),\n\t\t\tname: a.name,\n\t\t\ttitle: a.title,\n\t\t\ttype: a.type,\n\t\t\t...getStepNameAndId(a),\n\t\t}))\n\n\tallApps.sort((a, b) => {\n\t\t// order them by their stepId\n\t\tif (a.stepId > 0 && b.stepId > 0) return a.stepId - b.stepId\n\n\t\t// non-step apps should come after step apps\n\t\tif (a.stepId > 0) return -1\n\t\tif (b.stepId > 0) return 1\n\n\t\treturn 0\n\t})\n\n\tasync function getDiffProp() {\n\t\tif (!app1 || !app2) {\n\t\t\treturn {\n\t\t\t\tapp1: app1?.name,\n\t\t\t\tapp2: app2?.name,\n\t\t\t\tdiffCode: null,\n\t\t\t}\n\t\t}\n\t\tconst diffCode = await getDiffCode(app1, app2, {\n\t\t\t...cacheOptions,\n\t\t\tforceFresh: searchParams.get('forceFresh') === 'diff',\n\t\t}).catch((e) => {\n\t\t\tconsole.error(e)\n\t\t\treturn null\n\t\t})\n\t\treturn {\n\t\t\tapp1: app1.name,\n\t\t\tapp2: app2.name,\n\t\t\tdiffCode,\n\t\t}\n\t}\n\n\treturn data(\n\t\t{\n\t\t\ttype: params.type as 'problem' | 'solution',\n\t\t\texerciseStepApp,\n\t\t\tallApps,\n\t\t\t// defer this promise so that we don't block the response from being sent\n\t\t\tdiscordPostsPromise: fetchDiscordPosts({ request }),\n\t\t\tuserHasAccessPromise: userHasAccessToExerciseStep({\n\t\t\t\texerciseNumber: Number(params.exerciseNumber),\n\t\t\t\tstepNumber: Number(params.stepNumber),\n\t\t\t\trequest,\n\t\t\t\ttimings,\n\t\t\t}),\n\t\t\tplayground: playgroundApp\n\t\t\t\t? ({\n\t\t\t\t\t\ttype: 'playground',\n\t\t\t\t\t\tfullPath: playgroundApp.fullPath,\n\t\t\t\t\t\tdev: playgroundApp.dev,\n\t\t\t\t\t\ttest: playgroundApp.test,\n\t\t\t\t\t\ttitle: playgroundApp.title,\n\t\t\t\t\t\tname: playgroundApp.name,\n\t\t\t\t\t\tappName: playgroundApp.appName,\n\t\t\t\t\t\tisUpToDate: playgroundApp.isUpToDate,\n\t\t\t\t\t\tstackBlitzUrl: playgroundApp.stackBlitzUrl,\n\t\t\t\t\t\t...(await getAppRunningState(playgroundApp)),\n\t\t\t\t\t\t...getTestState(playgroundApp),\n\t\t\t\t\t} as const)\n\t\t\t\t: null,\n\t\t\tproblem: problemApp\n\t\t\t\t? ({\n\t\t\t\t\t\ttype: 'problem',\n\t\t\t\t\t\tfullPath: problemApp.fullPath,\n\t\t\t\t\t\tdev: problemApp.dev,\n\t\t\t\t\t\ttest: problemApp.test,\n\t\t\t\t\t\ttitle: problemApp.title,\n\t\t\t\t\t\tname: problemApp.name,\n\t\t\t\t\t\tstackBlitzUrl: problemApp.stackBlitzUrl,\n\t\t\t\t\t\t...(await getAppRunningState(problemApp)),\n\t\t\t\t\t} as const)\n\t\t\t\t: null,\n\t\t\tsolution: solutionApp\n\t\t\t\t? ({\n\t\t\t\t\t\ttype: 'solution',\n\t\t\t\t\t\tfullPath: solutionApp.fullPath,\n\t\t\t\t\t\tdev: solutionApp.dev,\n\t\t\t\t\t\ttest: solutionApp.test,\n\t\t\t\t\t\ttitle: solutionApp.title,\n\t\t\t\t\t\tname: solutionApp.name,\n\t\t\t\t\t\tstackBlitzUrl: solutionApp.stackBlitzUrl,\n\t\t\t\t\t\t...(await getAppRunningState(solutionApp)),\n\t\t\t\t\t} as const)\n\t\t\t\t: null,\n\t\t\tdiff: getDiffProp(),\n\t\t} as const,\n\t\t{\n\t\t\theaders: {\n\t\t\t\t'Server-Timing': getServerTimeHeader(timings),\n\t\t\t},\n\t\t},\n\t)\n}\n\nexport const headers: HeadersFunction = ({ loaderHeaders, parentHeaders }) => {\n\tconst headers = {\n\t\t'Server-Timing': combineServerTimings(loaderHeaders, parentHeaders),\n\t}\n\treturn headers\n}\n\nconst tabs = [\n\t'playground',\n\t'problem',\n\t'solution',\n\t'tests',\n\t'diff',\n\t'chat',\n] as const\nconst isValidPreview = (s: string | null): s is (typeof tabs)[number] =>\n\tBoolean(s && tabs.includes(s as (typeof tabs)[number]))\n\nexport default function ExercisePartRoute({\n\tloaderData,\n}: Route.ComponentProps) {\n\tconst { inBrowserBrowserRef } = useOutletContext<{\n\t\tinBrowserBrowserRef: React.RefObject<InBrowserBrowserRef | null>\n\t}>()\n\tconst workshopConfig = useWorkshopConfig()\n\tconst [searchParams] = useSearchParams()\n\n\tconst preview = searchParams.get('preview')\n\n\tconst altDown = useAltDown()\n\tconst navigate = useNavigate()\n\n\tfunction shouldHideTab(tab: (typeof tabs)[number]) {\n\t\tif (tab === 'tests') {\n\t\t\treturn (\n\t\t\t\tENV.EPICSHOP_DEPLOYED ||\n\t\t\t\t!loaderData.playground ||\n\t\t\t\tloaderData.playground.test.type === 'none'\n\t\t\t)\n\t\t}\n\t\tif (tab === 'problem' || tab === 'solution') {\n\t\t\tif (loaderData[tab]?.dev.type === 'none') return true\n\t\t\tif (ENV.EPICSHOP_DEPLOYED) {\n\t\t\t\tconst devType = loaderData[tab]?.dev.type\n\t\t\t\treturn (\n\t\t\t\t\tdevType !== 'browser' &&\n\t\t\t\t\tdevType !== 'export' &&\n\t\t\t\t\t!loaderData[tab]?.stackBlitzUrl\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t\tif (tab === 'playground' && ENV.EPICSHOP_DEPLOYED) return true\n\n\t\tif (tab === 'chat') {\n\t\t\treturn !workshopConfig.product.discordChannelId\n\t\t}\n\t\treturn false\n\t}\n\n\tfunction getTabStatus(\n\t\ttab: (typeof tabs)[number],\n\t): 'running' | 'passed' | 'failed' | null {\n\t\tif (tab === 'tests') {\n\t\t\tif (!loaderData.playground) return null\n\t\t\tconst { isTestRunning, testExitCode } = loaderData.playground\n\t\t\tif (isTestRunning) return 'running'\n\t\t\tif (testExitCode === 0) return 'passed'\n\t\t\tif (testExitCode !== null && testExitCode !== 0) return 'failed'\n\t\t\treturn null\n\t\t}\n\t\tif (tab === 'problem' || tab === 'solution' || tab === 'playground') {\n\t\t\tconst appData =\n\t\t\t\ttab === 'playground' ? loaderData.playground : loaderData[tab]\n\t\t\tif (appData?.isRunning) return 'running'\n\t\t}\n\t\treturn null\n\t}\n\n\tconst activeTab = isValidPreview(preview)\n\t\t? preview\n\t\t: tabs.find((t) => !shouldHideTab(t))\n\n\t// when alt is held down, the diff tab should open to the full-page diff view\n\t// between the problem and solution (this is more for the instructor than the student)\n\tconst altDiffUrl = `/diff?${new URLSearchParams({\n\t\tapp1: loaderData.problem?.name ?? '',\n\t\tapp2: loaderData.solution?.name ?? '',\n\t})}`\n\n\tfunction handleDiffTabClick(event: React.MouseEvent<HTMLAnchorElement>) {\n\t\tif (event.altKey && !event.ctrlKey && !event.shiftKey && !event.metaKey) {\n\t\t\tevent.preventDefault()\n\t\t\tvoid navigate(altDiffUrl)\n\t\t}\n\t}\n\n\tconst previewTabs = tabs.map((tab) => {\n\t\tconst hidden = shouldHideTab(tab)\n\t\tconst status = getTabStatus(tab)\n\t\tconst to =\n\t\t\ttab === 'diff' && altDown\n\t\t\t\t? altDiffUrl\n\t\t\t\t: `?${getPreviewSearchParams(searchParams, tab, 'playground')}`\n\t\treturn {\n\t\t\tid: tab,\n\t\t\tlabel: tab,\n\t\t\thidden,\n\t\t\tstatus,\n\t\t\tto,\n\t\t\tonClick: tab === 'diff' ? handleDiffTabClick : undefined,\n\t\t}\n\t})\n\n\treturn (\n\t\t<Tabs.Root\n\t\t\tclassName=\"relative flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden sm:col-span-1 sm:row-span-1\"\n\t\t\tvalue={activeTab}\n\t\t\t// intentionally no onValueChange here because the Link will trigger the\n\t\t\t// change.\n\t\t>\n\t\t\t<PreviewTabsList tabs={previewTabs} />\n\t\t\t<div className=\"relative z-10 flex min-h-0 flex-1 flex-col overflow-hidden\">\n\t\t\t\t<Tabs.Content\n\t\t\t\t\tvalue=\"playground\"\n\t\t\t\t\tclassName=\"radix-state-inactive:hidden flex min-h-0 w-full grow basis-0 items-stretch justify-center self-start\"\n\t\t\t\t\tforceMount\n\t\t\t\t>\n\t\t\t\t\t<Playground\n\t\t\t\t\t\tappInfo={loaderData.playground}\n\t\t\t\t\t\tproblemAppName={loaderData.problem?.name}\n\t\t\t\t\t\tinBrowserBrowserRef={inBrowserBrowserRef}\n\t\t\t\t\t\tallApps={loaderData.allApps}\n\t\t\t\t\t\tisUpToDate={loaderData.playground?.isUpToDate ?? false}\n\t\t\t\t\t/>\n\t\t\t\t</Tabs.Content>\n\t\t\t\t<Tabs.Content\n\t\t\t\t\tvalue=\"problem\"\n\t\t\t\t\tclassName=\"radix-state-inactive:hidden flex min-h-0 w-full grow basis-0 items-stretch justify-center self-start\"\n\t\t\t\t\tforceMount\n\t\t\t\t>\n\t\t\t\t\t<Preview\n\t\t\t\t\t\tappInfo={loaderData.problem}\n\t\t\t\t\t\tinBrowserBrowserRef={inBrowserBrowserRef}\n\t\t\t\t\t/>\n\t\t\t\t</Tabs.Content>\n\t\t\t\t<Tabs.Content\n\t\t\t\t\tvalue=\"solution\"\n\t\t\t\t\tclassName=\"radix-state-inactive:hidden flex min-h-0 w-full grow basis-0 items-stretch justify-center self-start\"\n\t\t\t\t\tforceMount\n\t\t\t\t>\n\t\t\t\t\t<Preview\n\t\t\t\t\t\tappInfo={loaderData.solution}\n\t\t\t\t\t\tinBrowserBrowserRef={inBrowserBrowserRef}\n\t\t\t\t\t/>\n\t\t\t\t</Tabs.Content>\n\t\t\t\t<Tabs.Content\n\t\t\t\t\tvalue=\"tests\"\n\t\t\t\t\tclassName=\"radix-state-inactive:hidden flex min-h-0 w-full grow basis-0 items-stretch justify-center self-start overflow-hidden\"\n\t\t\t\t>\n\t\t\t\t\t<Tests\n\t\t\t\t\t\tappInfo={loaderData.playground}\n\t\t\t\t\t\tproblemAppName={loaderData.problem?.name}\n\t\t\t\t\t\tallApps={loaderData.allApps}\n\t\t\t\t\t\tisUpToDate={loaderData.playground?.isUpToDate ?? false}\n\t\t\t\t\t\tuserHasAccessPromise={loaderData.userHasAccessPromise}\n\t\t\t\t\t/>\n\t\t\t\t</Tabs.Content>\n\t\t\t\t<Tabs.Content\n\t\t\t\t\tvalue=\"diff\"\n\t\t\t\t\tclassName=\"radix-state-inactive:hidden flex h-full min-h-0 w-full grow basis-0 items-stretch justify-center self-start\"\n\t\t\t\t>\n\t\t\t\t\t<Diff\n\t\t\t\t\t\tdiff={loaderData.diff}\n\t\t\t\t\t\tallApps={loaderData.allApps}\n\t\t\t\t\t\tuserHasAccessPromise={loaderData.userHasAccessPromise}\n\t\t\t\t\t/>\n\t\t\t\t</Tabs.Content>\n\t\t\t\t<Tabs.Content\n\t\t\t\t\tvalue=\"chat\"\n\t\t\t\t\tclassName=\"radix-state-inactive:hidden flex h-full min-h-0 w-full grow basis-0 items-stretch justify-center self-start\"\n\t\t\t\t>\n\t\t\t\t\t<DiscordChat discordPostsPromise={loaderData.discordPostsPromise} />\n\t\t\t\t</Tabs.Content>\n\t\t\t</div>\n\t\t</Tabs.Root>\n\t)\n}\n\nexport function ErrorBoundary() {\n\treturn (\n\t\t<GeneralErrorBoundary\n\t\t\tstatusHandlers={{\n\t\t\t\t404: () => <p>Sorry, we couldn't find an app here.</p>,\n\t\t\t}}\n\t\t/>\n\t)\n}\n"],"names":["tabs","isValidPreview","s","Boolean","includes","index","_UNSAFE_withComponentProps","loaderData","inBrowserBrowserRef","useOutletContext","workshopConfig","useWorkshopConfig","searchParams","useSearchParams","preview","get","altDown","useAltDown","navigate","useNavigate","shouldHideTab","tab","ENV","EPICSHOP_DEPLOYED","playground","test","type","dev","devType","stackBlitzUrl","product","discordChannelId","getTabStatus","isTestRunning","testExitCode","isRunning","activeTab","find","t","altDiffUrl","URLSearchParams","app1","problem","name","app2","solution","handleDiffTabClick","event","altKey","ctrlKey","shiftKey","metaKey","preventDefault","previewTabs","map","hidden","status","to","getPreviewSearchParams","id","label","onClick","jsxs","Tabs","className","value","children","jsx","PreviewTabsList","forceMount","Playground","appInfo","problemAppName","allApps","isUpToDate","Preview","Tests","userHasAccessPromise","Diff","diff","DiscordChat","discordPostsPromise","ErrorBoundary","_UNSAFE_withErrorBoundaryProps","GeneralErrorBoundary","statusHandlers"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index-BgExztV_.js","sources":["../../../app/routes/_app+/exercise+/$exerciseNumber_.$stepNumber.$type+/index.tsx"],"sourcesContent":["import {\n\tgetAppByName,\n\tgetAppDisplayName,\n\tgetApps,\n\tgetExerciseApp,\n\tisExerciseStepApp,\n\tisPlaygroundApp,\n\trequireExerciseApp,\n\ttype App,\n\ttype ExerciseStepApp,\n} from '@epic-web/workshop-utils/apps.server'\nimport { getDiffCode } from '@epic-web/workshop-utils/diff.server'\nimport { userHasAccessToExerciseStep } from '@epic-web/workshop-utils/epic-api.server'\nimport {\n\tcombineServerTimings,\n\tgetServerTimeHeader,\n\tmakeTimings,\n} from '@epic-web/workshop-utils/timing.server'\nimport * as Tabs from '@radix-ui/react-tabs'\nimport * as React from 'react'\nimport {\n\tuseNavigate,\n\tuseSearchParams,\n\tdata,\n\tredirect,\n\ttype HeadersFunction,\n\ttype LoaderFunctionArgs,\n\tuseOutletContext,\n} from 'react-router'\nimport { Diff } from '#app/components/diff.tsx'\nimport { DiscordChat } from '#app/components/discord-chat.tsx'\nimport { GeneralErrorBoundary } from '#app/components/error-boundary.tsx'\nimport { type InBrowserBrowserRef } from '#app/components/in-browser-browser.tsx'\nimport {\n\tgetPreviewSearchParams,\n\tPreviewTabsList,\n} from '#app/components/preview-tabs.tsx'\nimport { useWorkshopConfig } from '#app/components/workshop-config.tsx'\nimport { fetchDiscordPosts } from '#app/utils/discord.server.ts'\nimport { useAltDown } from '#app/utils/misc.tsx'\nimport { type Route } from './+types/index.ts'\nimport { Playground } from './__shared/playground.tsx'\nimport { Preview } from './__shared/preview.tsx'\nimport { Tests } from './__shared/tests.tsx'\nimport { getAppRunningState, getTestState } from './__shared/utils.tsx'\n\nexport async function loader({ request, params }: LoaderFunctionArgs) {\n\tconst timings = makeTimings('exerciseStepTypeIndexLoader')\n\tconst searchParams = new URL(request.url).searchParams\n\tconst cacheOptions = { request, timings }\n\n\tconst [exerciseStepApp, allAppsFull, problemApp, solutionApp] =\n\t\tawait Promise.all([\n\t\t\trequireExerciseApp(params, cacheOptions),\n\t\t\tgetApps(cacheOptions),\n\t\t\tgetExerciseApp({ ...params, type: 'problem' }, cacheOptions),\n\t\t\tgetExerciseApp({ ...params, type: 'solution' }, cacheOptions),\n\t\t])\n\n\tconst playgroundApp = allAppsFull.find(isPlaygroundApp)\n\tconst reqUrl = new URL(request.url)\n\n\tconst pathnameParam = reqUrl.searchParams.get('pathname')\n\tif (pathnameParam === '' || pathnameParam === '/') {\n\t\treqUrl.searchParams.delete('pathname')\n\t\tthrow redirect(reqUrl.toString())\n\t}\n\n\tconst app1Name = reqUrl.searchParams.get('app1')\n\tconst app2Name = reqUrl.searchParams.get('app2')\n\tconst app1 = app1Name\n\t\t? await getAppByName(app1Name)\n\t\t: playgroundApp || problemApp\n\tconst app2 = app2Name ? await getAppByName(app2Name) : solutionApp\n\n\tfunction getStepId(a: ExerciseStepApp) {\n\t\treturn (\n\t\t\ta.exerciseNumber * 1000 +\n\t\t\ta.stepNumber * 10 +\n\t\t\t(a.type === 'problem' ? 0 : 1)\n\t\t)\n\t}\n\n\tfunction getStepNameAndId(a: App) {\n\t\tif (isExerciseStepApp(a)) {\n\t\t\tconst exerciseNumberStr = String(a.exerciseNumber).padStart(2, '0')\n\t\t\tconst stepNumberStr = String(a.stepNumber).padStart(2, '0')\n\n\t\t\treturn {\n\t\t\t\tstepName: `${exerciseNumberStr}/${stepNumberStr}.${a.type}`,\n\t\t\t\tstepId: getStepId(a),\n\t\t\t}\n\t\t}\n\t\treturn { stepName: '', stepId: -1 }\n\t}\n\n\tconst allApps = allAppsFull\n\t\t.filter((a, i, ar) => ar.findIndex((b) => a.name === b.name) === i)\n\t\t.map((a) => ({\n\t\t\tdisplayName: getAppDisplayName(a, allAppsFull),\n\t\t\tname: a.name,\n\t\t\ttitle: a.title,\n\t\t\ttype: a.type,\n\t\t\t...getStepNameAndId(a),\n\t\t}))\n\n\tallApps.sort((a, b) => {\n\t\t// order them by their stepId\n\t\tif (a.stepId > 0 && b.stepId > 0) return a.stepId - b.stepId\n\n\t\t// non-step apps should come after step apps\n\t\tif (a.stepId > 0) return -1\n\t\tif (b.stepId > 0) return 1\n\n\t\treturn 0\n\t})\n\n\tasync function getDiffProp() {\n\t\tif (!app1 || !app2) {\n\t\t\treturn {\n\t\t\t\tapp1: app1?.name,\n\t\t\t\tapp2: app2?.name,\n\t\t\t\tdiffCode: null,\n\t\t\t}\n\t\t}\n\t\tconst diffCode = await getDiffCode(app1, app2, {\n\t\t\t...cacheOptions,\n\t\t\tforceFresh: searchParams.get('forceFresh') === 'diff',\n\t\t}).catch((e) => {\n\t\t\tconsole.error(e)\n\t\t\treturn null\n\t\t})\n\t\treturn {\n\t\t\tapp1: app1.name,\n\t\t\tapp2: app2.name,\n\t\t\tdiffCode,\n\t\t}\n\t}\n\n\treturn data(\n\t\t{\n\t\t\ttype: params.type as 'problem' | 'solution',\n\t\t\texerciseStepApp,\n\t\t\tallApps,\n\t\t\t// defer this promise so that we don't block the response from being sent\n\t\t\tdiscordPostsPromise: fetchDiscordPosts({ request }),\n\t\t\tuserHasAccessPromise: userHasAccessToExerciseStep({\n\t\t\t\texerciseNumber: Number(params.exerciseNumber),\n\t\t\t\tstepNumber: Number(params.stepNumber),\n\t\t\t\trequest,\n\t\t\t\ttimings,\n\t\t\t}),\n\t\t\tplayground: playgroundApp\n\t\t\t\t? ({\n\t\t\t\t\t\ttype: 'playground',\n\t\t\t\t\t\tfullPath: playgroundApp.fullPath,\n\t\t\t\t\t\tdev: playgroundApp.dev,\n\t\t\t\t\t\ttest: playgroundApp.test,\n\t\t\t\t\t\ttitle: playgroundApp.title,\n\t\t\t\t\t\tname: playgroundApp.name,\n\t\t\t\t\t\tappName: playgroundApp.appName,\n\t\t\t\t\t\tisUpToDate: playgroundApp.isUpToDate,\n\t\t\t\t\t\tstackBlitzUrl: playgroundApp.stackBlitzUrl,\n\t\t\t\t\t\t...(await getAppRunningState(playgroundApp)),\n\t\t\t\t\t\t...getTestState(playgroundApp),\n\t\t\t\t\t} as const)\n\t\t\t\t: null,\n\t\t\tproblem: problemApp\n\t\t\t\t? ({\n\t\t\t\t\t\ttype: 'problem',\n\t\t\t\t\t\tfullPath: problemApp.fullPath,\n\t\t\t\t\t\tdev: problemApp.dev,\n\t\t\t\t\t\ttest: problemApp.test,\n\t\t\t\t\t\ttitle: problemApp.title,\n\t\t\t\t\t\tname: problemApp.name,\n\t\t\t\t\t\tstackBlitzUrl: problemApp.stackBlitzUrl,\n\t\t\t\t\t\t...(await getAppRunningState(problemApp)),\n\t\t\t\t\t} as const)\n\t\t\t\t: null,\n\t\t\tsolution: solutionApp\n\t\t\t\t? ({\n\t\t\t\t\t\ttype: 'solution',\n\t\t\t\t\t\tfullPath: solutionApp.fullPath,\n\t\t\t\t\t\tdev: solutionApp.dev,\n\t\t\t\t\t\ttest: solutionApp.test,\n\t\t\t\t\t\ttitle: solutionApp.title,\n\t\t\t\t\t\tname: solutionApp.name,\n\t\t\t\t\t\tstackBlitzUrl: solutionApp.stackBlitzUrl,\n\t\t\t\t\t\t...(await getAppRunningState(solutionApp)),\n\t\t\t\t\t} as const)\n\t\t\t\t: null,\n\t\t\tdiff: getDiffProp(),\n\t\t} as const,\n\t\t{\n\t\t\theaders: {\n\t\t\t\t'Server-Timing': getServerTimeHeader(timings),\n\t\t\t},\n\t\t},\n\t)\n}\n\nexport const headers: HeadersFunction = ({ loaderHeaders, parentHeaders }) => {\n\tconst headers = {\n\t\t'Server-Timing': combineServerTimings(loaderHeaders, parentHeaders),\n\t}\n\treturn headers\n}\n\nconst tabs = [\n\t'playground',\n\t'problem',\n\t'solution',\n\t'tests',\n\t'diff',\n\t'chat',\n] as const\nconst isValidPreview = (s: string | null): s is (typeof tabs)[number] =>\n\tBoolean(s && tabs.includes(s as (typeof tabs)[number]))\n\nexport default function ExercisePartRoute({\n\tloaderData,\n}: Route.ComponentProps) {\n\tconst { inBrowserBrowserRef } = useOutletContext<{\n\t\tinBrowserBrowserRef: React.RefObject<InBrowserBrowserRef | null>\n\t}>()\n\tconst workshopConfig = useWorkshopConfig()\n\tconst [searchParams] = useSearchParams()\n\n\tconst preview = searchParams.get('preview')\n\n\tconst altDown = useAltDown()\n\tconst navigate = useNavigate()\n\n\tfunction shouldHideTab(tab: (typeof tabs)[number]) {\n\t\tif (tab === 'tests') {\n\t\t\treturn (\n\t\t\t\tENV.EPICSHOP_DEPLOYED ||\n\t\t\t\t!loaderData.playground ||\n\t\t\t\tloaderData.playground.test.type === 'none'\n\t\t\t)\n\t\t}\n\t\tif (tab === 'problem' || tab === 'solution') {\n\t\t\tif (loaderData[tab]?.dev.type === 'none') return true\n\t\t\tif (ENV.EPICSHOP_DEPLOYED) {\n\t\t\t\tconst devType = loaderData[tab]?.dev.type\n\t\t\t\treturn (\n\t\t\t\t\tdevType !== 'browser' &&\n\t\t\t\t\tdevType !== 'export' &&\n\t\t\t\t\t!loaderData[tab]?.stackBlitzUrl\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t\tif (tab === 'playground' && ENV.EPICSHOP_DEPLOYED) return true\n\n\t\tif (tab === 'chat') {\n\t\t\treturn !workshopConfig.product.discordChannelId\n\t\t}\n\t\treturn false\n\t}\n\n\tfunction getTabStatus(\n\t\ttab: (typeof tabs)[number],\n\t): 'running' | 'passed' | 'failed' | null {\n\t\tif (tab === 'tests') {\n\t\t\tif (!loaderData.playground) return null\n\t\t\tconst { isTestRunning, testExitCode } = loaderData.playground\n\t\t\tif (isTestRunning) return 'running'\n\t\t\tif (testExitCode === 0) return 'passed'\n\t\t\tif (testExitCode !== null && testExitCode !== 0) return 'failed'\n\t\t\treturn null\n\t\t}\n\t\tif (tab === 'problem' || tab === 'solution' || tab === 'playground') {\n\t\t\tconst appData =\n\t\t\t\ttab === 'playground' ? loaderData.playground : loaderData[tab]\n\t\t\tif (appData?.isRunning) return 'running'\n\t\t}\n\t\treturn null\n\t}\n\n\tconst activeTab = isValidPreview(preview)\n\t\t? preview\n\t\t: tabs.find((t) => !shouldHideTab(t))\n\n\t// when alt is held down, the diff tab should open to the full-page diff view\n\t// between the problem and solution (this is more for the instructor than the student)\n\tconst altDiffUrl = `/diff?${new URLSearchParams({\n\t\tapp1: loaderData.problem?.name ?? '',\n\t\tapp2: loaderData.solution?.name ?? '',\n\t})}`\n\n\tfunction handleDiffTabClick(event: React.MouseEvent<HTMLAnchorElement>) {\n\t\tif (event.altKey && !event.ctrlKey && !event.shiftKey && !event.metaKey) {\n\t\t\tevent.preventDefault()\n\t\t\tvoid navigate(altDiffUrl)\n\t\t}\n\t}\n\n\tconst previewTabs = tabs.map((tab) => {\n\t\tconst hidden = shouldHideTab(tab)\n\t\tconst status = getTabStatus(tab)\n\t\tconst to =\n\t\t\ttab === 'diff' && altDown\n\t\t\t\t? altDiffUrl\n\t\t\t\t: `?${getPreviewSearchParams(searchParams, tab, 'playground')}`\n\t\treturn {\n\t\t\tid: tab,\n\t\t\tlabel: tab,\n\t\t\thidden,\n\t\t\tstatus,\n\t\t\tto,\n\t\t\tonClick: tab === 'diff' ? handleDiffTabClick : undefined,\n\t\t}\n\t})\n\n\treturn (\n\t\t<Tabs.Root\n\t\t\tclassName=\"relative flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden sm:col-span-1 sm:row-span-1\"\n\t\t\tvalue={activeTab}\n\t\t\t// intentionally no onValueChange here because the Link will trigger the\n\t\t\t// change.\n\t\t>\n\t\t\t<PreviewTabsList tabs={previewTabs} />\n\t\t\t<div className=\"relative z-10 flex min-h-0 flex-1 flex-col overflow-hidden\">\n\t\t\t\t<Tabs.Content\n\t\t\t\t\tvalue=\"playground\"\n\t\t\t\t\tclassName=\"radix-state-inactive:hidden flex min-h-0 w-full grow basis-0 items-stretch justify-center self-start\"\n\t\t\t\t\tforceMount\n\t\t\t\t>\n\t\t\t\t\t<Playground\n\t\t\t\t\t\tappInfo={loaderData.playground}\n\t\t\t\t\t\tproblemAppName={loaderData.problem?.name}\n\t\t\t\t\t\tinBrowserBrowserRef={inBrowserBrowserRef}\n\t\t\t\t\t\tallApps={loaderData.allApps}\n\t\t\t\t\t\tisUpToDate={loaderData.playground?.isUpToDate ?? false}\n\t\t\t\t\t/>\n\t\t\t\t</Tabs.Content>\n\t\t\t\t<Tabs.Content\n\t\t\t\t\tvalue=\"problem\"\n\t\t\t\t\tclassName=\"radix-state-inactive:hidden flex min-h-0 w-full grow basis-0 items-stretch justify-center self-start\"\n\t\t\t\t\tforceMount\n\t\t\t\t>\n\t\t\t\t\t<Preview\n\t\t\t\t\t\tappInfo={loaderData.problem}\n\t\t\t\t\t\tinBrowserBrowserRef={inBrowserBrowserRef}\n\t\t\t\t\t/>\n\t\t\t\t</Tabs.Content>\n\t\t\t\t<Tabs.Content\n\t\t\t\t\tvalue=\"solution\"\n\t\t\t\t\tclassName=\"radix-state-inactive:hidden flex min-h-0 w-full grow basis-0 items-stretch justify-center self-start\"\n\t\t\t\t\tforceMount\n\t\t\t\t>\n\t\t\t\t\t<Preview\n\t\t\t\t\t\tappInfo={loaderData.solution}\n\t\t\t\t\t\tinBrowserBrowserRef={inBrowserBrowserRef}\n\t\t\t\t\t/>\n\t\t\t\t</Tabs.Content>\n\t\t\t\t<Tabs.Content\n\t\t\t\t\tvalue=\"tests\"\n\t\t\t\t\tclassName=\"radix-state-inactive:hidden flex min-h-0 w-full grow basis-0 items-stretch justify-center self-start overflow-hidden\"\n\t\t\t\t>\n\t\t\t\t\t<Tests\n\t\t\t\t\t\tappInfo={loaderData.playground}\n\t\t\t\t\t\tproblemAppName={loaderData.problem?.name}\n\t\t\t\t\t\tallApps={loaderData.allApps}\n\t\t\t\t\t\tisUpToDate={loaderData.playground?.isUpToDate ?? false}\n\t\t\t\t\t\tuserHasAccessPromise={loaderData.userHasAccessPromise}\n\t\t\t\t\t/>\n\t\t\t\t</Tabs.Content>\n\t\t\t\t<Tabs.Content\n\t\t\t\t\tvalue=\"diff\"\n\t\t\t\t\tclassName=\"radix-state-inactive:hidden flex h-full min-h-0 w-full grow basis-0 items-stretch justify-center self-start\"\n\t\t\t\t>\n\t\t\t\t\t<Diff\n\t\t\t\t\t\tdiff={loaderData.diff}\n\t\t\t\t\t\tallApps={loaderData.allApps}\n\t\t\t\t\t\tuserHasAccessPromise={loaderData.userHasAccessPromise}\n\t\t\t\t\t/>\n\t\t\t\t</Tabs.Content>\n\t\t\t\t<Tabs.Content\n\t\t\t\t\tvalue=\"chat\"\n\t\t\t\t\tclassName=\"radix-state-inactive:hidden flex h-full min-h-0 w-full grow basis-0 items-stretch justify-center self-start\"\n\t\t\t\t>\n\t\t\t\t\t<DiscordChat discordPostsPromise={loaderData.discordPostsPromise} />\n\t\t\t\t</Tabs.Content>\n\t\t\t</div>\n\t\t</Tabs.Root>\n\t)\n}\n\nexport function ErrorBoundary() {\n\treturn (\n\t\t<GeneralErrorBoundary\n\t\t\tstatusHandlers={{\n\t\t\t\t404: () => <p>Sorry, we couldn't find an app here.</p>,\n\t\t\t}}\n\t\t/>\n\t)\n}\n"],"names":["tabs","isValidPreview","s","Boolean","includes","index","_UNSAFE_withComponentProps","loaderData","inBrowserBrowserRef","useOutletContext","workshopConfig","useWorkshopConfig","searchParams","useSearchParams","preview","get","altDown","useAltDown","navigate","useNavigate","shouldHideTab","tab","ENV","EPICSHOP_DEPLOYED","playground","test","type","dev","devType","stackBlitzUrl","product","discordChannelId","getTabStatus","isTestRunning","testExitCode","isRunning","activeTab","find","t","altDiffUrl","URLSearchParams","app1","problem","name","app2","solution","handleDiffTabClick","event","altKey","ctrlKey","shiftKey","metaKey","preventDefault","previewTabs","map","hidden","status","to","getPreviewSearchParams","id","label","onClick","jsxs","Tabs","className","value","children","jsx","PreviewTabsList","forceMount","Playground","appInfo","problemAppName","allApps","isUpToDate","Preview","Tests","userHasAccessPromise","Diff","diff","DiscordChat","discordPostsPromise","ErrorBoundary","_UNSAFE_withErrorBoundaryProps","GeneralErrorBoundary","statusHandlers"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{w as R,k as E,F as N,L as P,f as T}from"./chunk-EPOLDU6W-BCLmut3y.js";import{j as e}from"./jsx-runtime-C5WNSv3b.js";import{I as c,a as S,c as j,e as F}from"./misc-W4055b-0.js";import{r as v}from"./index-CqIc3cxq.js";import{S as C}from"./tooltip-Tlsyx2YO.js";import{a as I}from"./progress-ILaVQtOO.js";import{g as $}from"./root-loader-BOzEMapJ.js";import"./index-vDCSPjrM.js";import"./pe-CIZUOJMr.js";const X={getSitemapEntries:()=>null},Y=({matches:s})=>[{title:`👷 | ${$(s)?.workshopTitle}`}];function B(s,t){return s.type==="unknown"&&t.type==="unknown"?0:s.type==="unknown"?-1:t.type==="unknown"?1:0}function W(s){switch(s.type){case"workshop-instructions":return"/";case"workshop-finished":return"/finished";case"instructions":return`/${s.exerciseNumber.toString().padStart(2,"0")}`;case"step":return`/${s.exerciseNumber.toString().padStart(2,"0")}/${s.stepNumber.toString().padStart(2,"0")}`;case"finished":return`/${s.exerciseNumber.toString().padStart(2,"0")}/finished`;default:return""}}const Z=R(function({loaderData:t}){const a=E(),l=I(),o=a.formData?.get("intent")==="inspect",g=a.formData?.get("intent")==="stop-inspect",f={completed:"bg-success",incomplete:"bg-warning"};return e.jsxs("div",{className:"flex flex-col gap-6 p-6",children:[e.jsxs("div",{className:"mb-2",children:[e.jsx("h1",{className:"text-foreground text-2xl font-bold",children:"Admin Panel"}),e.jsx("p",{className:"text-muted-foreground mt-1 text-sm",children:"Manage workshop settings and monitor processes"})]}),e.jsxs("div",{className:"grid gap-6 md:grid-cols-2",children:[Object.entries(t.sidecarProcesses).length>0&&e.jsxs(m,{children:[e.jsxs(u,{children:[e.jsx(x,{children:"Sidecar Processes"}),e.jsx(h,{children:"Background sidecar processes"})]}),e.jsx(p,{children:e.jsx("ul",{className:"flex flex-col gap-2",children:Object.entries(t.sidecarProcesses).map(([n,r])=>e.jsx(M,{name:n,pid:r.pid,running:r.running},n))})})]}),e.jsxs(m,{children:[e.jsxs(u,{children:[e.jsx(x,{children:"Commands"}),e.jsx(h,{children:"Workshop management actions"})]}),e.jsx(p,{children:e.jsxs("div",{className:"flex flex-col gap-3",children:[e.jsx(N,{method:"POST",children:e.jsxs(L,{name:"intent",value:"clear-caches",children:[e.jsx(c,{name:"Clear",className:"h-4 w-4"}),"Clear local caches"]})}),e.jsx(N,{method:"POST",children:e.jsxs(L,{name:"intent",value:"clear-data",className:"border-destructive bg-destructive/80 text-destructive-foreground hover:bg-destructive hover:text-destructive-foreground",doubleCheckClassName:"border-destructive bg-destructive text-destructive-foreground",children:[e.jsx(c,{name:"TriangleAlert",className:"h-4 w-4"}),"Clear all local data (including auth data)"]})}),t.inspectorRunning?e.jsx(N,{method:"POST",children:e.jsxs(y,{name:"intent",value:"stop-inspect",children:[e.jsx(c,{name:"Stop",className:"h-4 w-4"}),g?"Stopping inspector...":"Stop inspector"]})}):e.jsx(N,{method:"POST",children:e.jsx(y,{name:"intent",value:"inspect",children:o?"Starting inspector...":"Start inspector"})})]})})]}),e.jsxs(m,{children:[e.jsxs(u,{children:[e.jsx(x,{children:"Apps"}),e.jsx(h,{children:"Available workshop apps"})]}),e.jsx(p,{children:e.jsx("ul",{className:"scrollbar-thin scrollbar-thumb-scrollbar flex max-h-48 flex-col gap-2 overflow-y-auto",children:t.apps.length>0?t.apps.map(n=>e.jsxs("li",{className:"hover:bg-muted/50 flex items-center gap-3 rounded-md p-2",children:[t.processes[n.name]?e.jsx(i,{status:"running"}):e.jsx(i,{status:"stopped"}),e.jsx("span",{className:"font-mono text-sm",children:n.name})]},n.name)):e.jsx("p",{className:"text-muted-foreground text-sm",children:"No apps available"})})})]}),e.jsxs(m,{children:[e.jsxs(u,{children:[e.jsx(x,{children:"Processes"}),e.jsx(h,{children:"Running development processes"})]}),e.jsx(p,{children:e.jsx("ul",{className:"scrollbar-thin scrollbar-thumb-scrollbar flex max-h-48 flex-col gap-2 overflow-y-auto",children:Object.entries(t.processes).length>0?Object.entries(t.processes).map(([n,r])=>e.jsxs("li",{className:"border-border bg-muted/30 rounded-md border p-3",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(i,{status:"running"}),e.jsx("span",{className:"font-mono text-sm font-semibold",children:n})]}),e.jsxs("div",{className:"text-muted-foreground mt-2 flex flex-wrap gap-x-4 gap-y-1 text-xs",children:[e.jsxs("span",{children:[e.jsx("span",{className:"font-medium",children:"Port:"})," ",r.port]}),r.pid&&e.jsxs("span",{children:[e.jsx("span",{className:"font-medium",children:"PID:"})," ",r.pid]}),e.jsxs("span",{children:[e.jsx("span",{className:"font-medium",children:"Color:"})," ",e.jsx("span",{className:"inline-block h-3 w-3 rounded-full",style:{backgroundColor:r.color}})]})]})]},n)):e.jsx("p",{className:"text-muted-foreground text-sm",children:"No processes running"})})})]}),Object.entries(t.testProcesses).length>0&&e.jsxs(m,{className:"md:col-span-2",children:[e.jsxs(u,{children:[e.jsx(x,{children:"Test Processes"}),e.jsx(h,{children:"Test execution processes"})]}),e.jsx(p,{children:e.jsx("ul",{className:"scrollbar-thin scrollbar-thumb-scrollbar flex flex-col gap-2 overflow-y-auto",children:Object.entries(t.testProcesses).map(([n,r])=>e.jsxs("li",{className:"border-border bg-muted/30 rounded-md border p-3",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[r.exitCode===null||r.exitCode===void 0?e.jsx(i,{status:"running"}):r.exitCode===0?e.jsx(i,{status:"stopped"}):e.jsx(i,{status:"taken"}),e.jsx("span",{className:"font-mono text-sm font-semibold",children:n})]}),e.jsxs("div",{className:"text-muted-foreground mt-2 flex flex-wrap gap-x-4 gap-y-1 text-xs",children:[r.pid&&e.jsxs("span",{children:[e.jsx("span",{className:"font-medium",children:"PID:"})," ",r.pid]}),e.jsxs("span",{children:[e.jsx("span",{className:"font-medium",children:"Exit code:"})," ",r.exitCode===null||r.exitCode===void 0?"Running":r.exitCode]})]})]},n))})})]}),e.jsxs(m,{children:[e.jsxs(u,{children:[e.jsx(x,{children:"Progress"}),e.jsx(h,{children:"EpicWeb.dev lesson progress"})]}),e.jsx(p,{children:l?e.jsx("ul",{className:"scrollbar-thin scrollbar-thumb-scrollbar flex max-h-72 flex-col gap-2 overflow-y-auto",children:l.sort(B).map(n=>{const r=n.epicCompletedAt?"completed":"incomplete",d=[n.epicLessonSlug,n.epicCompletedAt?`(${n.epicCompletedAt})`:null].filter(Boolean).join(" ");return e.jsxs("li",{className:"hover:bg-muted/50 flex items-center gap-3 rounded-md p-2",children:[e.jsx("span",{className:S("h-3 w-3 shrink-0 rounded-full",f[r]),title:r}),n.type==="unknown"?e.jsxs("span",{className:"flex flex-1 items-center gap-2 truncate text-sm",children:[e.jsx("span",{className:"truncate",children:d}),e.jsx(C,{content:"This video is in the workshop on EpicWeb.dev, but not in the local workshop.",children:e.jsx(c,{name:"Close",className:"text-destructive h-4 w-4 shrink-0"})})]}):e.jsx(P,{to:W(n),className:"text-foreground flex-1 truncate text-sm hover:underline",children:d}),e.jsx(P,{to:n.epicLessonUrl,className:"text-muted-foreground hover:text-foreground shrink-0",children:e.jsx(c,{name:"ExternalLink",className:"h-4 w-4"})})]},n.epicLessonSlug)})}):e.jsx("p",{className:"text-muted-foreground text-sm",children:"No progress data"})})]})]})]})});function M({name:s,pid:t,running:a}){const l=T(),o=T(),[g,f]=v.useState("idle"),[n,r]=v.useState(!1),[d,O]=v.useState(null),w=l.state!=="idle",k=o.state!=="idle";v.useEffect(()=>{o.data&&O(o.data.logs??"")},[o.data]);const D=()=>{d&&navigator.clipboard.writeText(d).then(()=>{f("copied"),setTimeout(()=>f("idle"),2e3)}).catch(()=>{})},A=b=>{r(b),b&&o.submit({intent:"get-sidecar-logs",name:s},{method:"POST"})};return e.jsxs("li",{className:"border-border bg-muted/30 rounded-md border p-3",children:[e.jsxs("div",{className:"flex items-center justify-between gap-2",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[a?e.jsx(i,{status:"running"}):e.jsx(i,{status:"taken"}),e.jsx("span",{className:"font-mono text-sm font-semibold",children:s})]}),e.jsx("div",{className:"flex items-center gap-1",children:e.jsxs(l.Form,{method:"POST",children:[e.jsx("input",{type:"hidden",name:"intent",value:"restart-sidecar"}),e.jsx("input",{type:"hidden",name:"name",value:s}),e.jsx(C,{content:"Restart process",children:e.jsx("button",{type:"submit",disabled:w,className:"text-muted-foreground hover:text-foreground hover:bg-muted rounded p-1.5 transition-colors disabled:cursor-not-allowed disabled:opacity-50",children:e.jsx(c,{name:"Refresh",className:j("h-4 w-4",w&&"animate-spin")})})})]})})]}),e.jsxs("div",{className:"text-muted-foreground mt-2 flex flex-wrap gap-x-4 gap-y-1 text-xs",children:[t&&e.jsxs("span",{children:[e.jsx("span",{className:"font-medium",children:"PID:"})," ",t]}),e.jsxs("span",{children:[e.jsx("span",{className:"font-medium",children:"Status:"})," ",w?"Restarting...":a?"Running":"Failed"]})]}),e.jsxs("details",{className:"mt-3",open:n,onToggle:b=>A(b.currentTarget.open),children:[e.jsx("summary",{className:"text-muted-foreground hover:text-foreground cursor-pointer text-xs font-medium",children:k?"Loading logs...":"View logs"}),e.jsxs("div",{className:"mt-2",children:[e.jsx("div",{className:"mb-1 flex justify-end",children:e.jsx(C,{content:g==="copied"?"Copied!":"Copy logs",children:e.jsx("button",{type:"button",onClick:D,disabled:!d,className:j("text-muted-foreground hover:text-foreground hover:bg-muted rounded p-1 transition-colors disabled:cursor-not-allowed disabled:opacity-50",g==="copied"&&"text-success"),children:e.jsx(c,{name:g==="copied"?"CheckSmall":"Copy",className:"h-3.5 w-3.5"})})})}),e.jsx("pre",{className:"scrollbar-thin scrollbar-thumb-scrollbar bg-background max-h-96 overflow-auto rounded border p-2 text-xs",children:d||(k?"Loading...":"No logs available")})]})]})]})}function m({children:s,className:t}){return e.jsx("div",{className:j("border-border bg-card rounded-lg border p-6 shadow-sm",t),children:s})}function u({children:s}){return e.jsx("div",{className:"mb-4",children:s})}function x({children:s}){return e.jsx("h2",{className:"text-foreground text-lg font-semibold",children:s})}function h({children:s}){return e.jsx("p",{className:"text-muted-foreground mt-1 text-sm",children:s})}function p({children:s}){return e.jsx("div",{children:s})}function y({children:s,...t}){return e.jsx("button",{...t,className:j("border-border bg-background text-foreground hover:bg-muted hover:text-foreground focus:ring-ring inline-flex w-full items-center justify-center gap-2 rounded-md border px-4 py-2 text-sm font-medium transition-colors focus:ring-2 focus:ring-offset-2 focus:outline-none disabled:pointer-events-none disabled:opacity-50",t.className),children:s})}function L({children:s,doubleCheckClassName:t,...a}){const{doubleCheck:l,getButtonProps:o}=F();return e.jsx(y,{...o(a),className:j(a.className,l?t??"border-destructive bg-destructive text-destructive-foreground":null),children:l?e.jsxs(e.Fragment,{children:[e.jsx(c,{name:"TriangleAlert",className:"h-4 w-4"}),"Are you sure?"]}):s})}function i({status:s}){const t={running:{pinger:"bg-success/60",circle:"bg-success"},starting:{pinger:"bg-info/60",circle:"bg-info"},stopped:{circle:"bg-muted-foreground"},taken:{pinger:"bg-destructive/60",circle:"bg-destructive"}}[s];return e.jsxs("span",{className:"relative flex h-3 w-3 shrink-0",children:[t.pinger?e.jsx("span",{className:S("absolute inline-flex h-full w-full animate-ping rounded-full opacity-75",t.pinger)}):null,e.jsx("span",{className:S("relative inline-flex h-3 w-3 rounded-full",t.circle)})]})}export{Z as default,X as handle,Y as meta};
|
|
2
|
-
//# sourceMappingURL=index-
|
|
1
|
+
import{w as R,k as E,F as N,L as P,f as T}from"./chunk-EPOLDU6W-BCLmut3y.js";import{j as e}from"./jsx-runtime-C5WNSv3b.js";import{I as c,a as S,c as j,e as F}from"./misc-W4055b-0.js";import{r as v}from"./index-CqIc3cxq.js";import{S as C}from"./tooltip-Tlsyx2YO.js";import{a as I}from"./progress-BcA4i0iU.js";import{g as $}from"./root-loader-BOzEMapJ.js";import"./index-vDCSPjrM.js";import"./pe-CIZUOJMr.js";const X={getSitemapEntries:()=>null},Y=({matches:s})=>[{title:`👷 | ${$(s)?.workshopTitle}`}];function B(s,t){return s.type==="unknown"&&t.type==="unknown"?0:s.type==="unknown"?-1:t.type==="unknown"?1:0}function W(s){switch(s.type){case"workshop-instructions":return"/";case"workshop-finished":return"/finished";case"instructions":return`/${s.exerciseNumber.toString().padStart(2,"0")}`;case"step":return`/${s.exerciseNumber.toString().padStart(2,"0")}/${s.stepNumber.toString().padStart(2,"0")}`;case"finished":return`/${s.exerciseNumber.toString().padStart(2,"0")}/finished`;default:return""}}const Z=R(function({loaderData:t}){const a=E(),l=I(),o=a.formData?.get("intent")==="inspect",g=a.formData?.get("intent")==="stop-inspect",f={completed:"bg-success",incomplete:"bg-warning"};return e.jsxs("div",{className:"flex flex-col gap-6 p-6",children:[e.jsxs("div",{className:"mb-2",children:[e.jsx("h1",{className:"text-foreground text-2xl font-bold",children:"Admin Panel"}),e.jsx("p",{className:"text-muted-foreground mt-1 text-sm",children:"Manage workshop settings and monitor processes"})]}),e.jsxs("div",{className:"grid gap-6 md:grid-cols-2",children:[Object.entries(t.sidecarProcesses).length>0&&e.jsxs(m,{children:[e.jsxs(u,{children:[e.jsx(x,{children:"Sidecar Processes"}),e.jsx(h,{children:"Background sidecar processes"})]}),e.jsx(p,{children:e.jsx("ul",{className:"flex flex-col gap-2",children:Object.entries(t.sidecarProcesses).map(([n,r])=>e.jsx(M,{name:n,pid:r.pid,running:r.running},n))})})]}),e.jsxs(m,{children:[e.jsxs(u,{children:[e.jsx(x,{children:"Commands"}),e.jsx(h,{children:"Workshop management actions"})]}),e.jsx(p,{children:e.jsxs("div",{className:"flex flex-col gap-3",children:[e.jsx(N,{method:"POST",children:e.jsxs(L,{name:"intent",value:"clear-caches",children:[e.jsx(c,{name:"Clear",className:"h-4 w-4"}),"Clear local caches"]})}),e.jsx(N,{method:"POST",children:e.jsxs(L,{name:"intent",value:"clear-data",className:"border-destructive bg-destructive/80 text-destructive-foreground hover:bg-destructive hover:text-destructive-foreground",doubleCheckClassName:"border-destructive bg-destructive text-destructive-foreground",children:[e.jsx(c,{name:"TriangleAlert",className:"h-4 w-4"}),"Clear all local data (including auth data)"]})}),t.inspectorRunning?e.jsx(N,{method:"POST",children:e.jsxs(y,{name:"intent",value:"stop-inspect",children:[e.jsx(c,{name:"Stop",className:"h-4 w-4"}),g?"Stopping inspector...":"Stop inspector"]})}):e.jsx(N,{method:"POST",children:e.jsx(y,{name:"intent",value:"inspect",children:o?"Starting inspector...":"Start inspector"})})]})})]}),e.jsxs(m,{children:[e.jsxs(u,{children:[e.jsx(x,{children:"Apps"}),e.jsx(h,{children:"Available workshop apps"})]}),e.jsx(p,{children:e.jsx("ul",{className:"scrollbar-thin scrollbar-thumb-scrollbar flex max-h-48 flex-col gap-2 overflow-y-auto",children:t.apps.length>0?t.apps.map(n=>e.jsxs("li",{className:"hover:bg-muted/50 flex items-center gap-3 rounded-md p-2",children:[t.processes[n.name]?e.jsx(i,{status:"running"}):e.jsx(i,{status:"stopped"}),e.jsx("span",{className:"font-mono text-sm",children:n.name})]},n.name)):e.jsx("p",{className:"text-muted-foreground text-sm",children:"No apps available"})})})]}),e.jsxs(m,{children:[e.jsxs(u,{children:[e.jsx(x,{children:"Processes"}),e.jsx(h,{children:"Running development processes"})]}),e.jsx(p,{children:e.jsx("ul",{className:"scrollbar-thin scrollbar-thumb-scrollbar flex max-h-48 flex-col gap-2 overflow-y-auto",children:Object.entries(t.processes).length>0?Object.entries(t.processes).map(([n,r])=>e.jsxs("li",{className:"border-border bg-muted/30 rounded-md border p-3",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(i,{status:"running"}),e.jsx("span",{className:"font-mono text-sm font-semibold",children:n})]}),e.jsxs("div",{className:"text-muted-foreground mt-2 flex flex-wrap gap-x-4 gap-y-1 text-xs",children:[e.jsxs("span",{children:[e.jsx("span",{className:"font-medium",children:"Port:"})," ",r.port]}),r.pid&&e.jsxs("span",{children:[e.jsx("span",{className:"font-medium",children:"PID:"})," ",r.pid]}),e.jsxs("span",{children:[e.jsx("span",{className:"font-medium",children:"Color:"})," ",e.jsx("span",{className:"inline-block h-3 w-3 rounded-full",style:{backgroundColor:r.color}})]})]})]},n)):e.jsx("p",{className:"text-muted-foreground text-sm",children:"No processes running"})})})]}),Object.entries(t.testProcesses).length>0&&e.jsxs(m,{className:"md:col-span-2",children:[e.jsxs(u,{children:[e.jsx(x,{children:"Test Processes"}),e.jsx(h,{children:"Test execution processes"})]}),e.jsx(p,{children:e.jsx("ul",{className:"scrollbar-thin scrollbar-thumb-scrollbar flex flex-col gap-2 overflow-y-auto",children:Object.entries(t.testProcesses).map(([n,r])=>e.jsxs("li",{className:"border-border bg-muted/30 rounded-md border p-3",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[r.exitCode===null||r.exitCode===void 0?e.jsx(i,{status:"running"}):r.exitCode===0?e.jsx(i,{status:"stopped"}):e.jsx(i,{status:"taken"}),e.jsx("span",{className:"font-mono text-sm font-semibold",children:n})]}),e.jsxs("div",{className:"text-muted-foreground mt-2 flex flex-wrap gap-x-4 gap-y-1 text-xs",children:[r.pid&&e.jsxs("span",{children:[e.jsx("span",{className:"font-medium",children:"PID:"})," ",r.pid]}),e.jsxs("span",{children:[e.jsx("span",{className:"font-medium",children:"Exit code:"})," ",r.exitCode===null||r.exitCode===void 0?"Running":r.exitCode]})]})]},n))})})]}),e.jsxs(m,{children:[e.jsxs(u,{children:[e.jsx(x,{children:"Progress"}),e.jsx(h,{children:"EpicWeb.dev lesson progress"})]}),e.jsx(p,{children:l?e.jsx("ul",{className:"scrollbar-thin scrollbar-thumb-scrollbar flex max-h-72 flex-col gap-2 overflow-y-auto",children:l.sort(B).map(n=>{const r=n.epicCompletedAt?"completed":"incomplete",d=[n.epicLessonSlug,n.epicCompletedAt?`(${n.epicCompletedAt})`:null].filter(Boolean).join(" ");return e.jsxs("li",{className:"hover:bg-muted/50 flex items-center gap-3 rounded-md p-2",children:[e.jsx("span",{className:S("h-3 w-3 shrink-0 rounded-full",f[r]),title:r}),n.type==="unknown"?e.jsxs("span",{className:"flex flex-1 items-center gap-2 truncate text-sm",children:[e.jsx("span",{className:"truncate",children:d}),e.jsx(C,{content:"This video is in the workshop on EpicWeb.dev, but not in the local workshop.",children:e.jsx(c,{name:"Close",className:"text-destructive h-4 w-4 shrink-0"})})]}):e.jsx(P,{to:W(n),className:"text-foreground flex-1 truncate text-sm hover:underline",children:d}),e.jsx(P,{to:n.epicLessonUrl,className:"text-muted-foreground hover:text-foreground shrink-0",children:e.jsx(c,{name:"ExternalLink",className:"h-4 w-4"})})]},n.epicLessonSlug)})}):e.jsx("p",{className:"text-muted-foreground text-sm",children:"No progress data"})})]})]})]})});function M({name:s,pid:t,running:a}){const l=T(),o=T(),[g,f]=v.useState("idle"),[n,r]=v.useState(!1),[d,O]=v.useState(null),w=l.state!=="idle",k=o.state!=="idle";v.useEffect(()=>{o.data&&O(o.data.logs??"")},[o.data]);const D=()=>{d&&navigator.clipboard.writeText(d).then(()=>{f("copied"),setTimeout(()=>f("idle"),2e3)}).catch(()=>{})},A=b=>{r(b),b&&o.submit({intent:"get-sidecar-logs",name:s},{method:"POST"})};return e.jsxs("li",{className:"border-border bg-muted/30 rounded-md border p-3",children:[e.jsxs("div",{className:"flex items-center justify-between gap-2",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[a?e.jsx(i,{status:"running"}):e.jsx(i,{status:"taken"}),e.jsx("span",{className:"font-mono text-sm font-semibold",children:s})]}),e.jsx("div",{className:"flex items-center gap-1",children:e.jsxs(l.Form,{method:"POST",children:[e.jsx("input",{type:"hidden",name:"intent",value:"restart-sidecar"}),e.jsx("input",{type:"hidden",name:"name",value:s}),e.jsx(C,{content:"Restart process",children:e.jsx("button",{type:"submit",disabled:w,className:"text-muted-foreground hover:text-foreground hover:bg-muted rounded p-1.5 transition-colors disabled:cursor-not-allowed disabled:opacity-50",children:e.jsx(c,{name:"Refresh",className:j("h-4 w-4",w&&"animate-spin")})})})]})})]}),e.jsxs("div",{className:"text-muted-foreground mt-2 flex flex-wrap gap-x-4 gap-y-1 text-xs",children:[t&&e.jsxs("span",{children:[e.jsx("span",{className:"font-medium",children:"PID:"})," ",t]}),e.jsxs("span",{children:[e.jsx("span",{className:"font-medium",children:"Status:"})," ",w?"Restarting...":a?"Running":"Failed"]})]}),e.jsxs("details",{className:"mt-3",open:n,onToggle:b=>A(b.currentTarget.open),children:[e.jsx("summary",{className:"text-muted-foreground hover:text-foreground cursor-pointer text-xs font-medium",children:k?"Loading logs...":"View logs"}),e.jsxs("div",{className:"mt-2",children:[e.jsx("div",{className:"mb-1 flex justify-end",children:e.jsx(C,{content:g==="copied"?"Copied!":"Copy logs",children:e.jsx("button",{type:"button",onClick:D,disabled:!d,className:j("text-muted-foreground hover:text-foreground hover:bg-muted rounded p-1 transition-colors disabled:cursor-not-allowed disabled:opacity-50",g==="copied"&&"text-success"),children:e.jsx(c,{name:g==="copied"?"CheckSmall":"Copy",className:"h-3.5 w-3.5"})})})}),e.jsx("pre",{className:"scrollbar-thin scrollbar-thumb-scrollbar bg-background max-h-96 overflow-auto rounded border p-2 text-xs",children:d||(k?"Loading...":"No logs available")})]})]})]})}function m({children:s,className:t}){return e.jsx("div",{className:j("border-border bg-card rounded-lg border p-6 shadow-sm",t),children:s})}function u({children:s}){return e.jsx("div",{className:"mb-4",children:s})}function x({children:s}){return e.jsx("h2",{className:"text-foreground text-lg font-semibold",children:s})}function h({children:s}){return e.jsx("p",{className:"text-muted-foreground mt-1 text-sm",children:s})}function p({children:s}){return e.jsx("div",{children:s})}function y({children:s,...t}){return e.jsx("button",{...t,className:j("border-border bg-background text-foreground hover:bg-muted hover:text-foreground focus:ring-ring inline-flex w-full items-center justify-center gap-2 rounded-md border px-4 py-2 text-sm font-medium transition-colors focus:ring-2 focus:ring-offset-2 focus:outline-none disabled:pointer-events-none disabled:opacity-50",t.className),children:s})}function L({children:s,doubleCheckClassName:t,...a}){const{doubleCheck:l,getButtonProps:o}=F();return e.jsx(y,{...o(a),className:j(a.className,l?t??"border-destructive bg-destructive text-destructive-foreground":null),children:l?e.jsxs(e.Fragment,{children:[e.jsx(c,{name:"TriangleAlert",className:"h-4 w-4"}),"Are you sure?"]}):s})}function i({status:s}){const t={running:{pinger:"bg-success/60",circle:"bg-success"},starting:{pinger:"bg-info/60",circle:"bg-info"},stopped:{circle:"bg-muted-foreground"},taken:{pinger:"bg-destructive/60",circle:"bg-destructive"}}[s];return e.jsxs("span",{className:"relative flex h-3 w-3 shrink-0",children:[t.pinger?e.jsx("span",{className:S("absolute inline-flex h-full w-full animate-ping rounded-full opacity-75",t.pinger)}):null,e.jsx("span",{className:S("relative inline-flex h-3 w-3 rounded-full",t.circle)})]})}export{Z as default,X as handle,Y as meta};
|
|
2
|
+
//# sourceMappingURL=index-DroM8fzS.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index-DuFMeXSm.js","sources":["../../../app/routes/admin+/index.tsx"],"sourcesContent":["import { getApps } from '@epic-web/workshop-utils/apps.server'\nimport { getProcesses } from '@epic-web/workshop-utils/process-manager.server'\nimport {\n\tgetServerTimeHeader,\n\tmakeTimings,\n} from '@epic-web/workshop-utils/timing.server'\nimport { type SEOHandle } from '@nasa-gcn/remix-seo'\nimport { clsx } from 'clsx'\nimport * as React from 'react'\nimport { data, Form, Link, useFetcher, useNavigation } from 'react-router'\nimport { Icon } from '#app/components/icons.tsx'\nimport { SimpleTooltip } from '#app/components/ui/tooltip.tsx'\nimport {\n\tuseEpicProgress,\n\ttype SerializedProgress,\n} from '#app/routes/progress.tsx'\nimport { cn, ensureUndeployed, useDoubleCheck } from '#app/utils/misc.tsx'\nimport { getRootMatchLoaderData } from '#app/utils/root-loader.ts'\nimport { type Route } from './+types/index.tsx'\nimport {\n\tclearCaches,\n\tclearData,\n\tgetSidecarLogLines,\n\tisInspectorRunning,\n\trestartSidecar,\n\tstartInspector,\n\tstopInspector,\n} from './admin-utils.server.tsx'\n\nexport const handle: SEOHandle = {\n\tgetSitemapEntries: () => null,\n}\n\nexport const meta: Route.MetaFunction = ({ matches }) => {\n\tconst rootData = getRootMatchLoaderData(matches)\n\treturn [{ title: `👷 | ${rootData?.workshopTitle}` }]\n}\n\nexport async function loader({ request }: Route.LoaderArgs) {\n\tensureUndeployed()\n\tconst timings = makeTimings('adminLoader')\n\tconst apps = (await getApps({ request, timings })).filter(\n\t\t(a, i, ar) => ar.findIndex((b) => a.name === b.name) === i,\n\t)\n\tconst processes: Record<\n\t\tstring,\n\t\t{ port: number; pid?: number; color: string }\n\t> = {}\n\tconst testProcesses: Record<\n\t\tstring,\n\t\t{ pid?: number; exitCode?: number | null }\n\t> = {}\n\tfor (const [\n\t\tname,\n\t\t{ port, process, color },\n\t] of getProcesses().devProcesses.entries()) {\n\t\tprocesses[name] = { port, pid: process.pid, color }\n\t}\n\n\tfor (const [\n\t\tname,\n\t\t{ process, exitCode },\n\t] of getProcesses().testProcesses.entries()) {\n\t\ttestProcesses[name] = { pid: process?.pid, exitCode }\n\t}\n\n\tconst sidecarProcesses: Record<string, { pid?: number; running: boolean }> =\n\t\t{}\n\tfor (const [name, { process }] of getProcesses().sidecarProcesses.entries()) {\n\t\tsidecarProcesses[name] = {\n\t\t\tpid: process.pid,\n\t\t\trunning: process.exitCode === null,\n\t\t}\n\t}\n\n\treturn data(\n\t\t{\n\t\t\tapps,\n\t\t\tprocesses,\n\t\t\ttestProcesses,\n\t\t\tsidecarProcesses,\n\t\t\tinspectorRunning: isInspectorRunning(),\n\t\t},\n\t\t{\n\t\t\theaders: {\n\t\t\t\t'Server-Timing': getServerTimeHeader(timings),\n\t\t\t},\n\t\t},\n\t)\n}\n\nexport async function action({ request }: Route.ActionArgs) {\n\tensureUndeployed()\n\tconst formData = await request.formData()\n\tconst intent = formData.get('intent')\n\tswitch (intent) {\n\t\tcase 'clear-data': {\n\t\t\tawait clearData()\n\t\t\treturn { success: true }\n\t\t}\n\t\tcase 'clear-caches': {\n\t\t\tawait clearCaches()\n\t\t\treturn { success: true }\n\t\t}\n\t\tcase 'inspect': {\n\t\t\tawait startInspector()\n\t\t\treturn { success: true }\n\t\t}\n\t\tcase 'stop-inspect': {\n\t\t\tawait stopInspector()\n\t\t\treturn { success: true }\n\t\t}\n\t\tcase 'restart-sidecar': {\n\t\t\tconst name = formData.get('name')\n\t\t\tif (typeof name !== 'string') {\n\t\t\t\tthrow new Error('Sidecar name is required')\n\t\t\t}\n\t\t\tconst success = await restartSidecar(name)\n\t\t\treturn { success }\n\t\t}\n\t\tcase 'get-sidecar-logs': {\n\t\t\tconst name = formData.get('name')\n\t\t\tif (typeof name !== 'string') {\n\t\t\t\tthrow new Error('Sidecar name is required')\n\t\t\t}\n\t\t\tconst logs = getSidecarLogLines(name, 1000)\n\t\t\treturn { success: true, logs }\n\t\t}\n\t\tdefault: {\n\t\t\tthrow new Error(`Unknown intent: ${intent}`)\n\t\t}\n\t}\n}\n\nfunction sortProgress(a: SerializedProgress, b: SerializedProgress) {\n\treturn a.type === 'unknown' && b.type === 'unknown'\n\t\t? 0\n\t\t: a.type === 'unknown'\n\t\t\t? -1\n\t\t\t: b.type === 'unknown'\n\t\t\t\t? 1\n\t\t\t\t: 0\n}\n\nfunction linkProgress(progress: SerializedProgress) {\n\tswitch (progress.type) {\n\t\tcase 'workshop-instructions':\n\t\t\treturn '/'\n\t\tcase 'workshop-finished':\n\t\t\treturn '/finished'\n\t\tcase 'instructions':\n\t\t\treturn `/${progress.exerciseNumber.toString().padStart(2, '0')}`\n\t\tcase 'step':\n\t\t\treturn `/${progress.exerciseNumber\n\t\t\t\t.toString()\n\t\t\t\t.padStart(2, '0')}/${progress.stepNumber.toString().padStart(2, '0')}`\n\t\tcase 'finished':\n\t\t\treturn `/${progress.exerciseNumber.toString().padStart(2, '0')}/finished`\n\t\tdefault:\n\t\t\treturn ''\n\t}\n}\n\nexport default function AdminLayout({\n\tloaderData: data,\n}: Route.ComponentProps) {\n\tconst navigation = useNavigation()\n\tconst epicProgress = useEpicProgress()\n\n\tconst isStartingInspector = navigation.formData?.get('intent') === 'inspect'\n\tconst isStoppingInspector =\n\t\tnavigation.formData?.get('intent') === 'stop-inspect'\n\n\tconst progressStatus = {\n\t\tcompleted: 'bg-success',\n\t\tincomplete: 'bg-warning',\n\t}\n\n\treturn (\n\t\t<div className=\"flex flex-col gap-6 p-6\">\n\t\t\t<div className=\"mb-2\">\n\t\t\t\t<h1 className=\"text-foreground text-2xl font-bold\">Admin Panel</h1>\n\t\t\t\t<p className=\"text-muted-foreground mt-1 text-sm\">\n\t\t\t\t\tManage workshop settings and monitor processes\n\t\t\t\t</p>\n\t\t\t</div>\n\n\t\t\t<div className=\"grid gap-6 md:grid-cols-2\">\n\t\t\t\t{Object.entries(data.sidecarProcesses).length > 0 && (\n\t\t\t\t\t<Card>\n\t\t\t\t\t\t<CardHeader>\n\t\t\t\t\t\t\t<CardTitle>Sidecar Processes</CardTitle>\n\t\t\t\t\t\t\t<CardDescription>Background sidecar processes</CardDescription>\n\t\t\t\t\t\t</CardHeader>\n\t\t\t\t\t\t<CardContent>\n\t\t\t\t\t\t\t<ul className=\"flex flex-col gap-2\">\n\t\t\t\t\t\t\t\t{Object.entries(data.sidecarProcesses).map(([key, process]) => (\n\t\t\t\t\t\t\t\t\t<SidecarProcessItem\n\t\t\t\t\t\t\t\t\t\tkey={key}\n\t\t\t\t\t\t\t\t\t\tname={key}\n\t\t\t\t\t\t\t\t\t\tpid={process.pid}\n\t\t\t\t\t\t\t\t\t\trunning={process.running}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t</ul>\n\t\t\t\t\t\t</CardContent>\n\t\t\t\t\t</Card>\n\t\t\t\t)}\n\n\t\t\t\t<Card>\n\t\t\t\t\t<CardHeader>\n\t\t\t\t\t\t<CardTitle>Commands</CardTitle>\n\t\t\t\t\t\t<CardDescription>Workshop management actions</CardDescription>\n\t\t\t\t\t</CardHeader>\n\t\t\t\t\t<CardContent>\n\t\t\t\t\t\t<div className=\"flex flex-col gap-3\">\n\t\t\t\t\t\t\t<Form method=\"POST\">\n\t\t\t\t\t\t\t\t<DoubleCheckAdminButton name=\"intent\" value=\"clear-caches\">\n\t\t\t\t\t\t\t\t\t<Icon name=\"Clear\" className=\"h-4 w-4\" />\n\t\t\t\t\t\t\t\t\tClear local caches\n\t\t\t\t\t\t\t\t</DoubleCheckAdminButton>\n\t\t\t\t\t\t\t</Form>\n\t\t\t\t\t\t\t<Form method=\"POST\">\n\t\t\t\t\t\t\t\t<DoubleCheckAdminButton\n\t\t\t\t\t\t\t\t\tname=\"intent\"\n\t\t\t\t\t\t\t\t\tvalue=\"clear-data\"\n\t\t\t\t\t\t\t\t\tclassName=\"border-destructive bg-destructive/80 text-destructive-foreground hover:bg-destructive hover:text-destructive-foreground\"\n\t\t\t\t\t\t\t\t\tdoubleCheckClassName=\"border-destructive bg-destructive text-destructive-foreground\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<Icon name=\"TriangleAlert\" className=\"h-4 w-4\" />\n\t\t\t\t\t\t\t\t\tClear all local data (including auth data)\n\t\t\t\t\t\t\t\t</DoubleCheckAdminButton>\n\t\t\t\t\t\t\t</Form>\n\t\t\t\t\t\t\t{data.inspectorRunning ? (\n\t\t\t\t\t\t\t\t<Form method=\"POST\">\n\t\t\t\t\t\t\t\t\t<AdminButton name=\"intent\" value=\"stop-inspect\">\n\t\t\t\t\t\t\t\t\t\t<Icon name=\"Stop\" className=\"h-4 w-4\" />\n\t\t\t\t\t\t\t\t\t\t{isStoppingInspector\n\t\t\t\t\t\t\t\t\t\t\t? 'Stopping inspector...'\n\t\t\t\t\t\t\t\t\t\t\t: 'Stop inspector'}\n\t\t\t\t\t\t\t\t\t</AdminButton>\n\t\t\t\t\t\t\t\t</Form>\n\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t<Form method=\"POST\">\n\t\t\t\t\t\t\t\t\t<AdminButton name=\"intent\" value=\"inspect\">\n\t\t\t\t\t\t\t\t\t\t{isStartingInspector\n\t\t\t\t\t\t\t\t\t\t\t? 'Starting inspector...'\n\t\t\t\t\t\t\t\t\t\t\t: 'Start inspector'}\n\t\t\t\t\t\t\t\t\t</AdminButton>\n\t\t\t\t\t\t\t\t</Form>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</CardContent>\n\t\t\t\t</Card>\n\n\t\t\t\t<Card>\n\t\t\t\t\t<CardHeader>\n\t\t\t\t\t\t<CardTitle>Apps</CardTitle>\n\t\t\t\t\t\t<CardDescription>Available workshop apps</CardDescription>\n\t\t\t\t\t</CardHeader>\n\t\t\t\t\t<CardContent>\n\t\t\t\t\t\t<ul className=\"scrollbar-thin scrollbar-thumb-scrollbar flex max-h-48 flex-col gap-2 overflow-y-auto\">\n\t\t\t\t\t\t\t{data.apps.length > 0 ? (\n\t\t\t\t\t\t\t\tdata.apps.map((app) => (\n\t\t\t\t\t\t\t\t\t<li\n\t\t\t\t\t\t\t\t\t\tkey={app.name}\n\t\t\t\t\t\t\t\t\t\tclassName=\"hover:bg-muted/50 flex items-center gap-3 rounded-md p-2\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{data.processes[app.name] ? (\n\t\t\t\t\t\t\t\t\t\t\t<Pinger status=\"running\" />\n\t\t\t\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t\t\t\t<Pinger status=\"stopped\" />\n\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t<span className=\"font-mono text-sm\">{app.name}</span>\n\t\t\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t\t))\n\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t<p className=\"text-muted-foreground text-sm\">\n\t\t\t\t\t\t\t\t\tNo apps available\n\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</ul>\n\t\t\t\t\t</CardContent>\n\t\t\t\t</Card>\n\n\t\t\t\t<Card>\n\t\t\t\t\t<CardHeader>\n\t\t\t\t\t\t<CardTitle>Processes</CardTitle>\n\t\t\t\t\t\t<CardDescription>Running development processes</CardDescription>\n\t\t\t\t\t</CardHeader>\n\t\t\t\t\t<CardContent>\n\t\t\t\t\t\t<ul className=\"scrollbar-thin scrollbar-thumb-scrollbar flex max-h-48 flex-col gap-2 overflow-y-auto\">\n\t\t\t\t\t\t\t{Object.entries(data.processes).length > 0 ? (\n\t\t\t\t\t\t\t\tObject.entries(data.processes).map(([key, process]) => (\n\t\t\t\t\t\t\t\t\t<li\n\t\t\t\t\t\t\t\t\t\tkey={key}\n\t\t\t\t\t\t\t\t\t\tclassName=\"border-border bg-muted/30 rounded-md border p-3\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t\t\t\t\t<Pinger status=\"running\" />\n\t\t\t\t\t\t\t\t\t\t\t<span className=\"font-mono text-sm font-semibold\">\n\t\t\t\t\t\t\t\t\t\t\t\t{key}\n\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t<div className=\"text-muted-foreground mt-2 flex flex-wrap gap-x-4 gap-y-1 text-xs\">\n\t\t\t\t\t\t\t\t\t\t\t<span>\n\t\t\t\t\t\t\t\t\t\t\t\t<span className=\"font-medium\">Port:</span>{' '}\n\t\t\t\t\t\t\t\t\t\t\t\t{process.port}\n\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t{process.pid && (\n\t\t\t\t\t\t\t\t\t\t\t\t<span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<span className=\"font-medium\">PID:</span>{' '}\n\t\t\t\t\t\t\t\t\t\t\t\t\t{process.pid}\n\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t\t<span>\n\t\t\t\t\t\t\t\t\t\t\t\t<span className=\"font-medium\">Color:</span>{' '}\n\t\t\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\t\t\tclassName=\"inline-block h-3 w-3 rounded-full\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tstyle={{ backgroundColor: process.color }}\n\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t\t))\n\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t<p className=\"text-muted-foreground text-sm\">\n\t\t\t\t\t\t\t\t\tNo processes running\n\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</ul>\n\t\t\t\t\t</CardContent>\n\t\t\t\t</Card>\n\n\t\t\t\t{Object.entries(data.testProcesses).length > 0 && (\n\t\t\t\t\t<Card className=\"md:col-span-2\">\n\t\t\t\t\t\t<CardHeader>\n\t\t\t\t\t\t\t<CardTitle>Test Processes</CardTitle>\n\t\t\t\t\t\t\t<CardDescription>Test execution processes</CardDescription>\n\t\t\t\t\t\t</CardHeader>\n\t\t\t\t\t\t<CardContent>\n\t\t\t\t\t\t\t<ul className=\"scrollbar-thin scrollbar-thumb-scrollbar flex flex-col gap-2 overflow-y-auto\">\n\t\t\t\t\t\t\t\t{Object.entries(data.testProcesses).map(([key, process]) => (\n\t\t\t\t\t\t\t\t\t<li\n\t\t\t\t\t\t\t\t\t\tkey={key}\n\t\t\t\t\t\t\t\t\t\tclassName=\"border-border bg-muted/30 rounded-md border p-3\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t\t\t\t\t{process.exitCode === null ||\n\t\t\t\t\t\t\t\t\t\t\tprocess.exitCode === undefined ? (\n\t\t\t\t\t\t\t\t\t\t\t\t<Pinger status=\"running\" />\n\t\t\t\t\t\t\t\t\t\t\t) : process.exitCode === 0 ? (\n\t\t\t\t\t\t\t\t\t\t\t\t<Pinger status=\"stopped\" />\n\t\t\t\t\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t\t\t\t\t<Pinger status=\"taken\" />\n\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t\t<span className=\"font-mono text-sm font-semibold\">\n\t\t\t\t\t\t\t\t\t\t\t\t{key}\n\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t<div className=\"text-muted-foreground mt-2 flex flex-wrap gap-x-4 gap-y-1 text-xs\">\n\t\t\t\t\t\t\t\t\t\t\t{process.pid && (\n\t\t\t\t\t\t\t\t\t\t\t\t<span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<span className=\"font-medium\">PID:</span>{' '}\n\t\t\t\t\t\t\t\t\t\t\t\t\t{process.pid}\n\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t\t<span>\n\t\t\t\t\t\t\t\t\t\t\t\t<span className=\"font-medium\">Exit code:</span>{' '}\n\t\t\t\t\t\t\t\t\t\t\t\t{process.exitCode === null ||\n\t\t\t\t\t\t\t\t\t\t\t\tprocess.exitCode === undefined\n\t\t\t\t\t\t\t\t\t\t\t\t\t? 'Running'\n\t\t\t\t\t\t\t\t\t\t\t\t\t: process.exitCode}\n\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t</ul>\n\t\t\t\t\t\t</CardContent>\n\t\t\t\t\t</Card>\n\t\t\t\t)}\n\n\t\t\t\t<Card>\n\t\t\t\t\t<CardHeader>\n\t\t\t\t\t\t<CardTitle>Progress</CardTitle>\n\t\t\t\t\t\t<CardDescription>EpicWeb.dev lesson progress</CardDescription>\n\t\t\t\t\t</CardHeader>\n\t\t\t\t\t<CardContent>\n\t\t\t\t\t\t{epicProgress ? (\n\t\t\t\t\t\t\t<ul className=\"scrollbar-thin scrollbar-thumb-scrollbar flex max-h-72 flex-col gap-2 overflow-y-auto\">\n\t\t\t\t\t\t\t\t{epicProgress.sort(sortProgress).map((progress) => {\n\t\t\t\t\t\t\t\t\tconst status = progress.epicCompletedAt\n\t\t\t\t\t\t\t\t\t\t? 'completed'\n\t\t\t\t\t\t\t\t\t\t: 'incomplete'\n\t\t\t\t\t\t\t\t\tconst label = [\n\t\t\t\t\t\t\t\t\t\tprogress.epicLessonSlug,\n\t\t\t\t\t\t\t\t\t\tprogress.epicCompletedAt\n\t\t\t\t\t\t\t\t\t\t\t? `(${progress.epicCompletedAt})`\n\t\t\t\t\t\t\t\t\t\t\t: null,\n\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t\t.filter(Boolean)\n\t\t\t\t\t\t\t\t\t\t.join(' ')\n\t\t\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t\t\t<li\n\t\t\t\t\t\t\t\t\t\t\tkey={progress.epicLessonSlug}\n\t\t\t\t\t\t\t\t\t\t\tclassName=\"hover:bg-muted/50 flex items-center gap-3 rounded-md p-2\"\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\t\tclassName={clsx(\n\t\t\t\t\t\t\t\t\t\t\t\t\t'h-3 w-3 shrink-0 rounded-full',\n\t\t\t\t\t\t\t\t\t\t\t\t\tprogressStatus[status],\n\t\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t\t\ttitle={status}\n\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t{progress.type === 'unknown' ? (\n\t\t\t\t\t\t\t\t\t\t\t\t<span className=\"flex flex-1 items-center gap-2 truncate text-sm\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<span className=\"truncate\">{label}</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<SimpleTooltip content=\"This video is in the workshop on EpicWeb.dev, but not in the local workshop.\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<Icon\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tname=\"Close\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclassName=\"text-destructive h-4 w-4 shrink-0\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t\t\t</SimpleTooltip>\n\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t\t\t\t\t<Link\n\t\t\t\t\t\t\t\t\t\t\t\t\tto={linkProgress(progress)}\n\t\t\t\t\t\t\t\t\t\t\t\t\tclassName=\"text-foreground flex-1 truncate text-sm hover:underline\"\n\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t{label}\n\t\t\t\t\t\t\t\t\t\t\t\t</Link>\n\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t\t<Link\n\t\t\t\t\t\t\t\t\t\t\t\tto={progress.epicLessonUrl}\n\t\t\t\t\t\t\t\t\t\t\t\tclassName=\"text-muted-foreground hover:text-foreground shrink-0\"\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t<Icon name=\"ExternalLink\" className=\"h-4 w-4\" />\n\t\t\t\t\t\t\t\t\t\t\t</Link>\n\t\t\t\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t})}\n\t\t\t\t\t\t\t</ul>\n\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t<p className=\"text-muted-foreground text-sm\">No progress data</p>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</CardContent>\n\t\t\t\t</Card>\n\t\t\t</div>\n\t\t</div>\n\t)\n}\n\nfunction SidecarProcessItem({\n\tname,\n\tpid,\n\trunning,\n}: {\n\tname: string\n\tpid?: number\n\trunning: boolean\n}) {\n\tconst restartFetcher = useFetcher()\n\tconst logsFetcher = useFetcher<{ logs?: string }>()\n\tconst [copyStatus, setCopyStatus] = React.useState<'idle' | 'copied'>('idle')\n\tconst [logsOpen, setLogsOpen] = React.useState(false)\n\tconst [displayLogs, setDisplayLogs] = React.useState<string | null>(null)\n\n\tconst isRestarting = restartFetcher.state !== 'idle'\n\tconst isLoadingLogs = logsFetcher.state !== 'idle'\n\n\t// Update display logs when fetcher returns data\n\tReact.useEffect(() => {\n\t\tif (logsFetcher.data) {\n\t\t\tsetDisplayLogs(logsFetcher.data.logs ?? '')\n\t\t}\n\t}, [logsFetcher.data])\n\n\tconst handleCopyLogs = () => {\n\t\tif (!displayLogs) return\n\t\tnavigator.clipboard\n\t\t\t.writeText(displayLogs)\n\t\t\t.then(() => {\n\t\t\t\tsetCopyStatus('copied')\n\t\t\t\tsetTimeout(() => setCopyStatus('idle'), 2000)\n\t\t\t})\n\t\t\t.catch(() => {\n\t\t\t\t// silently fail\n\t\t\t})\n\t}\n\n\tconst handleToggleLogs = (open: boolean) => {\n\t\tsetLogsOpen(open)\n\t\tif (open) {\n\t\t\t// Fetch logs when opening\n\t\t\tvoid logsFetcher.submit(\n\t\t\t\t{ intent: 'get-sidecar-logs', name },\n\t\t\t\t{ method: 'POST' },\n\t\t\t)\n\t\t}\n\t}\n\n\treturn (\n\t\t<li className=\"border-border bg-muted/30 rounded-md border p-3\">\n\t\t\t<div className=\"flex items-center justify-between gap-2\">\n\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t{running ? <Pinger status=\"running\" /> : <Pinger status=\"taken\" />}\n\t\t\t\t\t<span className=\"font-mono text-sm font-semibold\">{name}</span>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"flex items-center gap-1\">\n\t\t\t\t\t<restartFetcher.Form method=\"POST\">\n\t\t\t\t\t\t<input type=\"hidden\" name=\"intent\" value=\"restart-sidecar\" />\n\t\t\t\t\t\t<input type=\"hidden\" name=\"name\" value={name} />\n\t\t\t\t\t\t<SimpleTooltip content=\"Restart process\">\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\ttype=\"submit\"\n\t\t\t\t\t\t\t\tdisabled={isRestarting}\n\t\t\t\t\t\t\t\tclassName=\"text-muted-foreground hover:text-foreground hover:bg-muted rounded p-1.5 transition-colors disabled:cursor-not-allowed disabled:opacity-50\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<Icon\n\t\t\t\t\t\t\t\t\tname=\"Refresh\"\n\t\t\t\t\t\t\t\t\tclassName={cn('h-4 w-4', isRestarting && 'animate-spin')}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t</SimpleTooltip>\n\t\t\t\t\t</restartFetcher.Form>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<div className=\"text-muted-foreground mt-2 flex flex-wrap gap-x-4 gap-y-1 text-xs\">\n\t\t\t\t{pid && (\n\t\t\t\t\t<span>\n\t\t\t\t\t\t<span className=\"font-medium\">PID:</span> {pid}\n\t\t\t\t\t</span>\n\t\t\t\t)}\n\t\t\t\t<span>\n\t\t\t\t\t<span className=\"font-medium\">Status:</span>{' '}\n\t\t\t\t\t{isRestarting ? 'Restarting...' : running ? 'Running' : 'Failed'}\n\t\t\t\t</span>\n\t\t\t</div>\n\t\t\t<details\n\t\t\t\tclassName=\"mt-3\"\n\t\t\t\topen={logsOpen}\n\t\t\t\tonToggle={(e) => handleToggleLogs(e.currentTarget.open)}\n\t\t\t>\n\t\t\t\t<summary className=\"text-muted-foreground hover:text-foreground cursor-pointer text-xs font-medium\">\n\t\t\t\t\t{isLoadingLogs ? 'Loading logs...' : 'View logs'}\n\t\t\t\t</summary>\n\t\t\t\t<div className=\"mt-2\">\n\t\t\t\t\t<div className=\"mb-1 flex justify-end\">\n\t\t\t\t\t\t<SimpleTooltip\n\t\t\t\t\t\t\tcontent={copyStatus === 'copied' ? 'Copied!' : 'Copy logs'}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\tonClick={handleCopyLogs}\n\t\t\t\t\t\t\t\tdisabled={!displayLogs}\n\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t'text-muted-foreground hover:text-foreground hover:bg-muted rounded p-1 transition-colors disabled:cursor-not-allowed disabled:opacity-50',\n\t\t\t\t\t\t\t\t\tcopyStatus === 'copied' && 'text-success',\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<Icon\n\t\t\t\t\t\t\t\t\tname={copyStatus === 'copied' ? 'CheckSmall' : 'Copy'}\n\t\t\t\t\t\t\t\t\tclassName=\"h-3.5 w-3.5\"\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t</SimpleTooltip>\n\t\t\t\t\t</div>\n\t\t\t\t\t<pre className=\"scrollbar-thin scrollbar-thumb-scrollbar bg-background max-h-96 overflow-auto rounded border p-2 text-xs\">\n\t\t\t\t\t\t{displayLogs ||\n\t\t\t\t\t\t\t(isLoadingLogs ? 'Loading...' : 'No logs available')}\n\t\t\t\t\t</pre>\n\t\t\t\t</div>\n\t\t\t</details>\n\t\t</li>\n\t)\n}\n\nfunction Card({\n\tchildren,\n\tclassName,\n}: {\n\tchildren: React.ReactNode\n\tclassName?: string\n}) {\n\treturn (\n\t\t<div\n\t\t\tclassName={cn(\n\t\t\t\t'border-border bg-card rounded-lg border p-6 shadow-sm',\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t>\n\t\t\t{children}\n\t\t</div>\n\t)\n}\n\nfunction CardHeader({ children }: { children: React.ReactNode }) {\n\treturn <div className=\"mb-4\">{children}</div>\n}\n\nfunction CardTitle({ children }: { children: React.ReactNode }) {\n\treturn <h2 className=\"text-foreground text-lg font-semibold\">{children}</h2>\n}\n\nfunction CardDescription({ children }: { children: React.ReactNode }) {\n\treturn <p className=\"text-muted-foreground mt-1 text-sm\">{children}</p>\n}\n\nfunction CardContent({ children }: { children: React.ReactNode }) {\n\treturn <div>{children}</div>\n}\n\nfunction AdminButton({\n\tchildren,\n\t...props\n}: React.ComponentPropsWithoutRef<'button'>) {\n\treturn (\n\t\t<button\n\t\t\t{...props}\n\t\t\tclassName={cn(\n\t\t\t\t'border-border bg-background text-foreground hover:bg-muted hover:text-foreground focus:ring-ring inline-flex w-full items-center justify-center gap-2 rounded-md border px-4 py-2 text-sm font-medium transition-colors focus:ring-2 focus:ring-offset-2 focus:outline-none disabled:pointer-events-none disabled:opacity-50',\n\t\t\t\tprops.className,\n\t\t\t)}\n\t\t>\n\t\t\t{children}\n\t\t</button>\n\t)\n}\n\nfunction DoubleCheckAdminButton({\n\tchildren,\n\tdoubleCheckClassName,\n\t...props\n}: React.ComponentPropsWithoutRef<'button'> & {\n\tdoubleCheckClassName?: string\n}) {\n\tconst { doubleCheck, getButtonProps } = useDoubleCheck()\n\n\treturn (\n\t\t<AdminButton\n\t\t\t{...getButtonProps(props)}\n\t\t\tclassName={cn(\n\t\t\t\tprops.className,\n\t\t\t\tdoubleCheck\n\t\t\t\t\t? (doubleCheckClassName ??\n\t\t\t\t\t\t\t'border-destructive bg-destructive text-destructive-foreground')\n\t\t\t\t\t: null,\n\t\t\t)}\n\t\t>\n\t\t\t{doubleCheck ? (\n\t\t\t\t<>\n\t\t\t\t\t<Icon name=\"TriangleAlert\" className=\"h-4 w-4\" />\n\t\t\t\t\tAre you sure?\n\t\t\t\t</>\n\t\t\t) : (\n\t\t\t\tchildren\n\t\t\t)}\n\t\t</AdminButton>\n\t)\n}\n\nfunction Pinger({\n\tstatus,\n}: {\n\tstatus: 'running' | 'starting' | 'stopped' | 'taken'\n}) {\n\tconst colors = {\n\t\trunning: {\n\t\t\tpinger: 'bg-success/60',\n\t\t\tcircle: 'bg-success',\n\t\t},\n\t\tstarting: {\n\t\t\tpinger: 'bg-info/60',\n\t\t\tcircle: 'bg-info',\n\t\t},\n\t\tstopped: {\n\t\t\tcircle: 'bg-muted-foreground',\n\t\t},\n\t\ttaken: {\n\t\t\tpinger: 'bg-destructive/60',\n\t\t\tcircle: 'bg-destructive',\n\t\t},\n\t}[status]\n\treturn (\n\t\t<span className=\"relative flex h-3 w-3 shrink-0\">\n\t\t\t{colors.pinger ? (\n\t\t\t\t<span\n\t\t\t\t\tclassName={clsx(\n\t\t\t\t\t\t'absolute inline-flex h-full w-full animate-ping rounded-full opacity-75',\n\t\t\t\t\t\tcolors.pinger,\n\t\t\t\t\t)}\n\t\t\t\t/>\n\t\t\t) : null}\n\t\t\t<span\n\t\t\t\tclassName={clsx(\n\t\t\t\t\t'relative inline-flex h-3 w-3 rounded-full',\n\t\t\t\t\tcolors.circle,\n\t\t\t\t)}\n\t\t\t/>\n\t\t</span>\n\t)\n}\n"],"names":["handle","getSitemapEntries","meta","matches","title","getRootMatchLoaderData","workshopTitle","sortProgress","a","b","type","linkProgress","progress","exerciseNumber","toString","padStart","stepNumber","index","_UNSAFE_withComponentProps","loaderData","data","navigation","useNavigation","epicProgress","useEpicProgress","isStartingInspector","formData","get","isStoppingInspector","progressStatus","completed","incomplete","jsxs","className","children","jsx","Object","entries","sidecarProcesses","length","Card","CardHeader","CardTitle","CardDescription","CardContent","map","key","process","SidecarProcessItem","name","pid","running","Form","method","DoubleCheckAdminButton","value","Icon","doubleCheckClassName","inspectorRunning","AdminButton","apps","app","processes","Pinger","status","port","style","backgroundColor","color","testProcesses","exitCode","sort","epicCompletedAt","label","epicLessonSlug","filter","Boolean","join","clsx","SimpleTooltip","content","Link","to","epicLessonUrl","restartFetcher","useFetcher","logsFetcher","copyStatus","setCopyStatus","React","logsOpen","setLogsOpen","displayLogs","setDisplayLogs","isRestarting","state","isLoadingLogs","logs","handleCopyLogs","navigator","clipboard","writeText","then","setTimeout","catch","handleToggleLogs","open","submit","intent","disabled","cn","onToggle","e","currentTarget","onClick","props","doubleCheck","getButtonProps","useDoubleCheck","Fragment","colors","pinger","circle","starting","stopped","taken"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index-DroM8fzS.js","sources":["../../../app/routes/admin+/index.tsx"],"sourcesContent":["import { getApps } from '@epic-web/workshop-utils/apps.server'\nimport { getProcesses } from '@epic-web/workshop-utils/process-manager.server'\nimport {\n\tgetServerTimeHeader,\n\tmakeTimings,\n} from '@epic-web/workshop-utils/timing.server'\nimport { type SEOHandle } from '@nasa-gcn/remix-seo'\nimport { clsx } from 'clsx'\nimport * as React from 'react'\nimport { data, Form, Link, useFetcher, useNavigation } from 'react-router'\nimport { Icon } from '#app/components/icons.tsx'\nimport { SimpleTooltip } from '#app/components/ui/tooltip.tsx'\nimport {\n\tuseEpicProgress,\n\ttype SerializedProgress,\n} from '#app/routes/progress.tsx'\nimport { cn, ensureUndeployed, useDoubleCheck } from '#app/utils/misc.tsx'\nimport { getRootMatchLoaderData } from '#app/utils/root-loader.ts'\nimport { type Route } from './+types/index.tsx'\nimport {\n\tclearCaches,\n\tclearData,\n\tgetSidecarLogLines,\n\tisInspectorRunning,\n\trestartSidecar,\n\tstartInspector,\n\tstopInspector,\n} from './admin-utils.server.tsx'\n\nexport const handle: SEOHandle = {\n\tgetSitemapEntries: () => null,\n}\n\nexport const meta: Route.MetaFunction = ({ matches }) => {\n\tconst rootData = getRootMatchLoaderData(matches)\n\treturn [{ title: `👷 | ${rootData?.workshopTitle}` }]\n}\n\nexport async function loader({ request }: Route.LoaderArgs) {\n\tensureUndeployed()\n\tconst timings = makeTimings('adminLoader')\n\tconst apps = (await getApps({ request, timings })).filter(\n\t\t(a, i, ar) => ar.findIndex((b) => a.name === b.name) === i,\n\t)\n\tconst processes: Record<\n\t\tstring,\n\t\t{ port: number; pid?: number; color: string }\n\t> = {}\n\tconst testProcesses: Record<\n\t\tstring,\n\t\t{ pid?: number; exitCode?: number | null }\n\t> = {}\n\tfor (const [\n\t\tname,\n\t\t{ port, process, color },\n\t] of getProcesses().devProcesses.entries()) {\n\t\tprocesses[name] = { port, pid: process.pid, color }\n\t}\n\n\tfor (const [\n\t\tname,\n\t\t{ process, exitCode },\n\t] of getProcesses().testProcesses.entries()) {\n\t\ttestProcesses[name] = { pid: process?.pid, exitCode }\n\t}\n\n\tconst sidecarProcesses: Record<string, { pid?: number; running: boolean }> =\n\t\t{}\n\tfor (const [name, { process }] of getProcesses().sidecarProcesses.entries()) {\n\t\tsidecarProcesses[name] = {\n\t\t\tpid: process.pid,\n\t\t\trunning: process.exitCode === null,\n\t\t}\n\t}\n\n\treturn data(\n\t\t{\n\t\t\tapps,\n\t\t\tprocesses,\n\t\t\ttestProcesses,\n\t\t\tsidecarProcesses,\n\t\t\tinspectorRunning: isInspectorRunning(),\n\t\t},\n\t\t{\n\t\t\theaders: {\n\t\t\t\t'Server-Timing': getServerTimeHeader(timings),\n\t\t\t},\n\t\t},\n\t)\n}\n\nexport async function action({ request }: Route.ActionArgs) {\n\tensureUndeployed()\n\tconst formData = await request.formData()\n\tconst intent = formData.get('intent')\n\tswitch (intent) {\n\t\tcase 'clear-data': {\n\t\t\tawait clearData()\n\t\t\treturn { success: true }\n\t\t}\n\t\tcase 'clear-caches': {\n\t\t\tawait clearCaches()\n\t\t\treturn { success: true }\n\t\t}\n\t\tcase 'inspect': {\n\t\t\tawait startInspector()\n\t\t\treturn { success: true }\n\t\t}\n\t\tcase 'stop-inspect': {\n\t\t\tawait stopInspector()\n\t\t\treturn { success: true }\n\t\t}\n\t\tcase 'restart-sidecar': {\n\t\t\tconst name = formData.get('name')\n\t\t\tif (typeof name !== 'string') {\n\t\t\t\tthrow new Error('Sidecar name is required')\n\t\t\t}\n\t\t\tconst success = await restartSidecar(name)\n\t\t\treturn { success }\n\t\t}\n\t\tcase 'get-sidecar-logs': {\n\t\t\tconst name = formData.get('name')\n\t\t\tif (typeof name !== 'string') {\n\t\t\t\tthrow new Error('Sidecar name is required')\n\t\t\t}\n\t\t\tconst logs = getSidecarLogLines(name, 1000)\n\t\t\treturn { success: true, logs }\n\t\t}\n\t\tdefault: {\n\t\t\tthrow new Error(`Unknown intent: ${intent}`)\n\t\t}\n\t}\n}\n\nfunction sortProgress(a: SerializedProgress, b: SerializedProgress) {\n\treturn a.type === 'unknown' && b.type === 'unknown'\n\t\t? 0\n\t\t: a.type === 'unknown'\n\t\t\t? -1\n\t\t\t: b.type === 'unknown'\n\t\t\t\t? 1\n\t\t\t\t: 0\n}\n\nfunction linkProgress(progress: SerializedProgress) {\n\tswitch (progress.type) {\n\t\tcase 'workshop-instructions':\n\t\t\treturn '/'\n\t\tcase 'workshop-finished':\n\t\t\treturn '/finished'\n\t\tcase 'instructions':\n\t\t\treturn `/${progress.exerciseNumber.toString().padStart(2, '0')}`\n\t\tcase 'step':\n\t\t\treturn `/${progress.exerciseNumber\n\t\t\t\t.toString()\n\t\t\t\t.padStart(2, '0')}/${progress.stepNumber.toString().padStart(2, '0')}`\n\t\tcase 'finished':\n\t\t\treturn `/${progress.exerciseNumber.toString().padStart(2, '0')}/finished`\n\t\tdefault:\n\t\t\treturn ''\n\t}\n}\n\nexport default function AdminLayout({\n\tloaderData: data,\n}: Route.ComponentProps) {\n\tconst navigation = useNavigation()\n\tconst epicProgress = useEpicProgress()\n\n\tconst isStartingInspector = navigation.formData?.get('intent') === 'inspect'\n\tconst isStoppingInspector =\n\t\tnavigation.formData?.get('intent') === 'stop-inspect'\n\n\tconst progressStatus = {\n\t\tcompleted: 'bg-success',\n\t\tincomplete: 'bg-warning',\n\t}\n\n\treturn (\n\t\t<div className=\"flex flex-col gap-6 p-6\">\n\t\t\t<div className=\"mb-2\">\n\t\t\t\t<h1 className=\"text-foreground text-2xl font-bold\">Admin Panel</h1>\n\t\t\t\t<p className=\"text-muted-foreground mt-1 text-sm\">\n\t\t\t\t\tManage workshop settings and monitor processes\n\t\t\t\t</p>\n\t\t\t</div>\n\n\t\t\t<div className=\"grid gap-6 md:grid-cols-2\">\n\t\t\t\t{Object.entries(data.sidecarProcesses).length > 0 && (\n\t\t\t\t\t<Card>\n\t\t\t\t\t\t<CardHeader>\n\t\t\t\t\t\t\t<CardTitle>Sidecar Processes</CardTitle>\n\t\t\t\t\t\t\t<CardDescription>Background sidecar processes</CardDescription>\n\t\t\t\t\t\t</CardHeader>\n\t\t\t\t\t\t<CardContent>\n\t\t\t\t\t\t\t<ul className=\"flex flex-col gap-2\">\n\t\t\t\t\t\t\t\t{Object.entries(data.sidecarProcesses).map(([key, process]) => (\n\t\t\t\t\t\t\t\t\t<SidecarProcessItem\n\t\t\t\t\t\t\t\t\t\tkey={key}\n\t\t\t\t\t\t\t\t\t\tname={key}\n\t\t\t\t\t\t\t\t\t\tpid={process.pid}\n\t\t\t\t\t\t\t\t\t\trunning={process.running}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t</ul>\n\t\t\t\t\t\t</CardContent>\n\t\t\t\t\t</Card>\n\t\t\t\t)}\n\n\t\t\t\t<Card>\n\t\t\t\t\t<CardHeader>\n\t\t\t\t\t\t<CardTitle>Commands</CardTitle>\n\t\t\t\t\t\t<CardDescription>Workshop management actions</CardDescription>\n\t\t\t\t\t</CardHeader>\n\t\t\t\t\t<CardContent>\n\t\t\t\t\t\t<div className=\"flex flex-col gap-3\">\n\t\t\t\t\t\t\t<Form method=\"POST\">\n\t\t\t\t\t\t\t\t<DoubleCheckAdminButton name=\"intent\" value=\"clear-caches\">\n\t\t\t\t\t\t\t\t\t<Icon name=\"Clear\" className=\"h-4 w-4\" />\n\t\t\t\t\t\t\t\t\tClear local caches\n\t\t\t\t\t\t\t\t</DoubleCheckAdminButton>\n\t\t\t\t\t\t\t</Form>\n\t\t\t\t\t\t\t<Form method=\"POST\">\n\t\t\t\t\t\t\t\t<DoubleCheckAdminButton\n\t\t\t\t\t\t\t\t\tname=\"intent\"\n\t\t\t\t\t\t\t\t\tvalue=\"clear-data\"\n\t\t\t\t\t\t\t\t\tclassName=\"border-destructive bg-destructive/80 text-destructive-foreground hover:bg-destructive hover:text-destructive-foreground\"\n\t\t\t\t\t\t\t\t\tdoubleCheckClassName=\"border-destructive bg-destructive text-destructive-foreground\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<Icon name=\"TriangleAlert\" className=\"h-4 w-4\" />\n\t\t\t\t\t\t\t\t\tClear all local data (including auth data)\n\t\t\t\t\t\t\t\t</DoubleCheckAdminButton>\n\t\t\t\t\t\t\t</Form>\n\t\t\t\t\t\t\t{data.inspectorRunning ? (\n\t\t\t\t\t\t\t\t<Form method=\"POST\">\n\t\t\t\t\t\t\t\t\t<AdminButton name=\"intent\" value=\"stop-inspect\">\n\t\t\t\t\t\t\t\t\t\t<Icon name=\"Stop\" className=\"h-4 w-4\" />\n\t\t\t\t\t\t\t\t\t\t{isStoppingInspector\n\t\t\t\t\t\t\t\t\t\t\t? 'Stopping inspector...'\n\t\t\t\t\t\t\t\t\t\t\t: 'Stop inspector'}\n\t\t\t\t\t\t\t\t\t</AdminButton>\n\t\t\t\t\t\t\t\t</Form>\n\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t<Form method=\"POST\">\n\t\t\t\t\t\t\t\t\t<AdminButton name=\"intent\" value=\"inspect\">\n\t\t\t\t\t\t\t\t\t\t{isStartingInspector\n\t\t\t\t\t\t\t\t\t\t\t? 'Starting inspector...'\n\t\t\t\t\t\t\t\t\t\t\t: 'Start inspector'}\n\t\t\t\t\t\t\t\t\t</AdminButton>\n\t\t\t\t\t\t\t\t</Form>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</CardContent>\n\t\t\t\t</Card>\n\n\t\t\t\t<Card>\n\t\t\t\t\t<CardHeader>\n\t\t\t\t\t\t<CardTitle>Apps</CardTitle>\n\t\t\t\t\t\t<CardDescription>Available workshop apps</CardDescription>\n\t\t\t\t\t</CardHeader>\n\t\t\t\t\t<CardContent>\n\t\t\t\t\t\t<ul className=\"scrollbar-thin scrollbar-thumb-scrollbar flex max-h-48 flex-col gap-2 overflow-y-auto\">\n\t\t\t\t\t\t\t{data.apps.length > 0 ? (\n\t\t\t\t\t\t\t\tdata.apps.map((app) => (\n\t\t\t\t\t\t\t\t\t<li\n\t\t\t\t\t\t\t\t\t\tkey={app.name}\n\t\t\t\t\t\t\t\t\t\tclassName=\"hover:bg-muted/50 flex items-center gap-3 rounded-md p-2\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{data.processes[app.name] ? (\n\t\t\t\t\t\t\t\t\t\t\t<Pinger status=\"running\" />\n\t\t\t\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t\t\t\t<Pinger status=\"stopped\" />\n\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t<span className=\"font-mono text-sm\">{app.name}</span>\n\t\t\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t\t))\n\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t<p className=\"text-muted-foreground text-sm\">\n\t\t\t\t\t\t\t\t\tNo apps available\n\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</ul>\n\t\t\t\t\t</CardContent>\n\t\t\t\t</Card>\n\n\t\t\t\t<Card>\n\t\t\t\t\t<CardHeader>\n\t\t\t\t\t\t<CardTitle>Processes</CardTitle>\n\t\t\t\t\t\t<CardDescription>Running development processes</CardDescription>\n\t\t\t\t\t</CardHeader>\n\t\t\t\t\t<CardContent>\n\t\t\t\t\t\t<ul className=\"scrollbar-thin scrollbar-thumb-scrollbar flex max-h-48 flex-col gap-2 overflow-y-auto\">\n\t\t\t\t\t\t\t{Object.entries(data.processes).length > 0 ? (\n\t\t\t\t\t\t\t\tObject.entries(data.processes).map(([key, process]) => (\n\t\t\t\t\t\t\t\t\t<li\n\t\t\t\t\t\t\t\t\t\tkey={key}\n\t\t\t\t\t\t\t\t\t\tclassName=\"border-border bg-muted/30 rounded-md border p-3\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t\t\t\t\t<Pinger status=\"running\" />\n\t\t\t\t\t\t\t\t\t\t\t<span className=\"font-mono text-sm font-semibold\">\n\t\t\t\t\t\t\t\t\t\t\t\t{key}\n\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t<div className=\"text-muted-foreground mt-2 flex flex-wrap gap-x-4 gap-y-1 text-xs\">\n\t\t\t\t\t\t\t\t\t\t\t<span>\n\t\t\t\t\t\t\t\t\t\t\t\t<span className=\"font-medium\">Port:</span>{' '}\n\t\t\t\t\t\t\t\t\t\t\t\t{process.port}\n\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t{process.pid && (\n\t\t\t\t\t\t\t\t\t\t\t\t<span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<span className=\"font-medium\">PID:</span>{' '}\n\t\t\t\t\t\t\t\t\t\t\t\t\t{process.pid}\n\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t\t<span>\n\t\t\t\t\t\t\t\t\t\t\t\t<span className=\"font-medium\">Color:</span>{' '}\n\t\t\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\t\t\tclassName=\"inline-block h-3 w-3 rounded-full\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tstyle={{ backgroundColor: process.color }}\n\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t\t))\n\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t<p className=\"text-muted-foreground text-sm\">\n\t\t\t\t\t\t\t\t\tNo processes running\n\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</ul>\n\t\t\t\t\t</CardContent>\n\t\t\t\t</Card>\n\n\t\t\t\t{Object.entries(data.testProcesses).length > 0 && (\n\t\t\t\t\t<Card className=\"md:col-span-2\">\n\t\t\t\t\t\t<CardHeader>\n\t\t\t\t\t\t\t<CardTitle>Test Processes</CardTitle>\n\t\t\t\t\t\t\t<CardDescription>Test execution processes</CardDescription>\n\t\t\t\t\t\t</CardHeader>\n\t\t\t\t\t\t<CardContent>\n\t\t\t\t\t\t\t<ul className=\"scrollbar-thin scrollbar-thumb-scrollbar flex flex-col gap-2 overflow-y-auto\">\n\t\t\t\t\t\t\t\t{Object.entries(data.testProcesses).map(([key, process]) => (\n\t\t\t\t\t\t\t\t\t<li\n\t\t\t\t\t\t\t\t\t\tkey={key}\n\t\t\t\t\t\t\t\t\t\tclassName=\"border-border bg-muted/30 rounded-md border p-3\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t\t\t\t\t{process.exitCode === null ||\n\t\t\t\t\t\t\t\t\t\t\tprocess.exitCode === undefined ? (\n\t\t\t\t\t\t\t\t\t\t\t\t<Pinger status=\"running\" />\n\t\t\t\t\t\t\t\t\t\t\t) : process.exitCode === 0 ? (\n\t\t\t\t\t\t\t\t\t\t\t\t<Pinger status=\"stopped\" />\n\t\t\t\t\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t\t\t\t\t<Pinger status=\"taken\" />\n\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t\t<span className=\"font-mono text-sm font-semibold\">\n\t\t\t\t\t\t\t\t\t\t\t\t{key}\n\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t<div className=\"text-muted-foreground mt-2 flex flex-wrap gap-x-4 gap-y-1 text-xs\">\n\t\t\t\t\t\t\t\t\t\t\t{process.pid && (\n\t\t\t\t\t\t\t\t\t\t\t\t<span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<span className=\"font-medium\">PID:</span>{' '}\n\t\t\t\t\t\t\t\t\t\t\t\t\t{process.pid}\n\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t\t<span>\n\t\t\t\t\t\t\t\t\t\t\t\t<span className=\"font-medium\">Exit code:</span>{' '}\n\t\t\t\t\t\t\t\t\t\t\t\t{process.exitCode === null ||\n\t\t\t\t\t\t\t\t\t\t\t\tprocess.exitCode === undefined\n\t\t\t\t\t\t\t\t\t\t\t\t\t? 'Running'\n\t\t\t\t\t\t\t\t\t\t\t\t\t: process.exitCode}\n\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t</ul>\n\t\t\t\t\t\t</CardContent>\n\t\t\t\t\t</Card>\n\t\t\t\t)}\n\n\t\t\t\t<Card>\n\t\t\t\t\t<CardHeader>\n\t\t\t\t\t\t<CardTitle>Progress</CardTitle>\n\t\t\t\t\t\t<CardDescription>EpicWeb.dev lesson progress</CardDescription>\n\t\t\t\t\t</CardHeader>\n\t\t\t\t\t<CardContent>\n\t\t\t\t\t\t{epicProgress ? (\n\t\t\t\t\t\t\t<ul className=\"scrollbar-thin scrollbar-thumb-scrollbar flex max-h-72 flex-col gap-2 overflow-y-auto\">\n\t\t\t\t\t\t\t\t{epicProgress.sort(sortProgress).map((progress) => {\n\t\t\t\t\t\t\t\t\tconst status = progress.epicCompletedAt\n\t\t\t\t\t\t\t\t\t\t? 'completed'\n\t\t\t\t\t\t\t\t\t\t: 'incomplete'\n\t\t\t\t\t\t\t\t\tconst label = [\n\t\t\t\t\t\t\t\t\t\tprogress.epicLessonSlug,\n\t\t\t\t\t\t\t\t\t\tprogress.epicCompletedAt\n\t\t\t\t\t\t\t\t\t\t\t? `(${progress.epicCompletedAt})`\n\t\t\t\t\t\t\t\t\t\t\t: null,\n\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t\t.filter(Boolean)\n\t\t\t\t\t\t\t\t\t\t.join(' ')\n\t\t\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t\t\t<li\n\t\t\t\t\t\t\t\t\t\t\tkey={progress.epicLessonSlug}\n\t\t\t\t\t\t\t\t\t\t\tclassName=\"hover:bg-muted/50 flex items-center gap-3 rounded-md p-2\"\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\t\tclassName={clsx(\n\t\t\t\t\t\t\t\t\t\t\t\t\t'h-3 w-3 shrink-0 rounded-full',\n\t\t\t\t\t\t\t\t\t\t\t\t\tprogressStatus[status],\n\t\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t\t\ttitle={status}\n\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t{progress.type === 'unknown' ? (\n\t\t\t\t\t\t\t\t\t\t\t\t<span className=\"flex flex-1 items-center gap-2 truncate text-sm\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<span className=\"truncate\">{label}</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<SimpleTooltip content=\"This video is in the workshop on EpicWeb.dev, but not in the local workshop.\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<Icon\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tname=\"Close\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclassName=\"text-destructive h-4 w-4 shrink-0\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t\t\t</SimpleTooltip>\n\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t\t\t\t\t<Link\n\t\t\t\t\t\t\t\t\t\t\t\t\tto={linkProgress(progress)}\n\t\t\t\t\t\t\t\t\t\t\t\t\tclassName=\"text-foreground flex-1 truncate text-sm hover:underline\"\n\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t{label}\n\t\t\t\t\t\t\t\t\t\t\t\t</Link>\n\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t\t<Link\n\t\t\t\t\t\t\t\t\t\t\t\tto={progress.epicLessonUrl}\n\t\t\t\t\t\t\t\t\t\t\t\tclassName=\"text-muted-foreground hover:text-foreground shrink-0\"\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t<Icon name=\"ExternalLink\" className=\"h-4 w-4\" />\n\t\t\t\t\t\t\t\t\t\t\t</Link>\n\t\t\t\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t})}\n\t\t\t\t\t\t\t</ul>\n\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t<p className=\"text-muted-foreground text-sm\">No progress data</p>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</CardContent>\n\t\t\t\t</Card>\n\t\t\t</div>\n\t\t</div>\n\t)\n}\n\nfunction SidecarProcessItem({\n\tname,\n\tpid,\n\trunning,\n}: {\n\tname: string\n\tpid?: number\n\trunning: boolean\n}) {\n\tconst restartFetcher = useFetcher()\n\tconst logsFetcher = useFetcher<{ logs?: string }>()\n\tconst [copyStatus, setCopyStatus] = React.useState<'idle' | 'copied'>('idle')\n\tconst [logsOpen, setLogsOpen] = React.useState(false)\n\tconst [displayLogs, setDisplayLogs] = React.useState<string | null>(null)\n\n\tconst isRestarting = restartFetcher.state !== 'idle'\n\tconst isLoadingLogs = logsFetcher.state !== 'idle'\n\n\t// Update display logs when fetcher returns data\n\tReact.useEffect(() => {\n\t\tif (logsFetcher.data) {\n\t\t\tsetDisplayLogs(logsFetcher.data.logs ?? '')\n\t\t}\n\t}, [logsFetcher.data])\n\n\tconst handleCopyLogs = () => {\n\t\tif (!displayLogs) return\n\t\tnavigator.clipboard\n\t\t\t.writeText(displayLogs)\n\t\t\t.then(() => {\n\t\t\t\tsetCopyStatus('copied')\n\t\t\t\tsetTimeout(() => setCopyStatus('idle'), 2000)\n\t\t\t})\n\t\t\t.catch(() => {\n\t\t\t\t// silently fail\n\t\t\t})\n\t}\n\n\tconst handleToggleLogs = (open: boolean) => {\n\t\tsetLogsOpen(open)\n\t\tif (open) {\n\t\t\t// Fetch logs when opening\n\t\t\tvoid logsFetcher.submit(\n\t\t\t\t{ intent: 'get-sidecar-logs', name },\n\t\t\t\t{ method: 'POST' },\n\t\t\t)\n\t\t}\n\t}\n\n\treturn (\n\t\t<li className=\"border-border bg-muted/30 rounded-md border p-3\">\n\t\t\t<div className=\"flex items-center justify-between gap-2\">\n\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t{running ? <Pinger status=\"running\" /> : <Pinger status=\"taken\" />}\n\t\t\t\t\t<span className=\"font-mono text-sm font-semibold\">{name}</span>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"flex items-center gap-1\">\n\t\t\t\t\t<restartFetcher.Form method=\"POST\">\n\t\t\t\t\t\t<input type=\"hidden\" name=\"intent\" value=\"restart-sidecar\" />\n\t\t\t\t\t\t<input type=\"hidden\" name=\"name\" value={name} />\n\t\t\t\t\t\t<SimpleTooltip content=\"Restart process\">\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\ttype=\"submit\"\n\t\t\t\t\t\t\t\tdisabled={isRestarting}\n\t\t\t\t\t\t\t\tclassName=\"text-muted-foreground hover:text-foreground hover:bg-muted rounded p-1.5 transition-colors disabled:cursor-not-allowed disabled:opacity-50\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<Icon\n\t\t\t\t\t\t\t\t\tname=\"Refresh\"\n\t\t\t\t\t\t\t\t\tclassName={cn('h-4 w-4', isRestarting && 'animate-spin')}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t</SimpleTooltip>\n\t\t\t\t\t</restartFetcher.Form>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<div className=\"text-muted-foreground mt-2 flex flex-wrap gap-x-4 gap-y-1 text-xs\">\n\t\t\t\t{pid && (\n\t\t\t\t\t<span>\n\t\t\t\t\t\t<span className=\"font-medium\">PID:</span> {pid}\n\t\t\t\t\t</span>\n\t\t\t\t)}\n\t\t\t\t<span>\n\t\t\t\t\t<span className=\"font-medium\">Status:</span>{' '}\n\t\t\t\t\t{isRestarting ? 'Restarting...' : running ? 'Running' : 'Failed'}\n\t\t\t\t</span>\n\t\t\t</div>\n\t\t\t<details\n\t\t\t\tclassName=\"mt-3\"\n\t\t\t\topen={logsOpen}\n\t\t\t\tonToggle={(e) => handleToggleLogs(e.currentTarget.open)}\n\t\t\t>\n\t\t\t\t<summary className=\"text-muted-foreground hover:text-foreground cursor-pointer text-xs font-medium\">\n\t\t\t\t\t{isLoadingLogs ? 'Loading logs...' : 'View logs'}\n\t\t\t\t</summary>\n\t\t\t\t<div className=\"mt-2\">\n\t\t\t\t\t<div className=\"mb-1 flex justify-end\">\n\t\t\t\t\t\t<SimpleTooltip\n\t\t\t\t\t\t\tcontent={copyStatus === 'copied' ? 'Copied!' : 'Copy logs'}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\tonClick={handleCopyLogs}\n\t\t\t\t\t\t\t\tdisabled={!displayLogs}\n\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t'text-muted-foreground hover:text-foreground hover:bg-muted rounded p-1 transition-colors disabled:cursor-not-allowed disabled:opacity-50',\n\t\t\t\t\t\t\t\t\tcopyStatus === 'copied' && 'text-success',\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<Icon\n\t\t\t\t\t\t\t\t\tname={copyStatus === 'copied' ? 'CheckSmall' : 'Copy'}\n\t\t\t\t\t\t\t\t\tclassName=\"h-3.5 w-3.5\"\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t</SimpleTooltip>\n\t\t\t\t\t</div>\n\t\t\t\t\t<pre className=\"scrollbar-thin scrollbar-thumb-scrollbar bg-background max-h-96 overflow-auto rounded border p-2 text-xs\">\n\t\t\t\t\t\t{displayLogs ||\n\t\t\t\t\t\t\t(isLoadingLogs ? 'Loading...' : 'No logs available')}\n\t\t\t\t\t</pre>\n\t\t\t\t</div>\n\t\t\t</details>\n\t\t</li>\n\t)\n}\n\nfunction Card({\n\tchildren,\n\tclassName,\n}: {\n\tchildren: React.ReactNode\n\tclassName?: string\n}) {\n\treturn (\n\t\t<div\n\t\t\tclassName={cn(\n\t\t\t\t'border-border bg-card rounded-lg border p-6 shadow-sm',\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t>\n\t\t\t{children}\n\t\t</div>\n\t)\n}\n\nfunction CardHeader({ children }: { children: React.ReactNode }) {\n\treturn <div className=\"mb-4\">{children}</div>\n}\n\nfunction CardTitle({ children }: { children: React.ReactNode }) {\n\treturn <h2 className=\"text-foreground text-lg font-semibold\">{children}</h2>\n}\n\nfunction CardDescription({ children }: { children: React.ReactNode }) {\n\treturn <p className=\"text-muted-foreground mt-1 text-sm\">{children}</p>\n}\n\nfunction CardContent({ children }: { children: React.ReactNode }) {\n\treturn <div>{children}</div>\n}\n\nfunction AdminButton({\n\tchildren,\n\t...props\n}: React.ComponentPropsWithoutRef<'button'>) {\n\treturn (\n\t\t<button\n\t\t\t{...props}\n\t\t\tclassName={cn(\n\t\t\t\t'border-border bg-background text-foreground hover:bg-muted hover:text-foreground focus:ring-ring inline-flex w-full items-center justify-center gap-2 rounded-md border px-4 py-2 text-sm font-medium transition-colors focus:ring-2 focus:ring-offset-2 focus:outline-none disabled:pointer-events-none disabled:opacity-50',\n\t\t\t\tprops.className,\n\t\t\t)}\n\t\t>\n\t\t\t{children}\n\t\t</button>\n\t)\n}\n\nfunction DoubleCheckAdminButton({\n\tchildren,\n\tdoubleCheckClassName,\n\t...props\n}: React.ComponentPropsWithoutRef<'button'> & {\n\tdoubleCheckClassName?: string\n}) {\n\tconst { doubleCheck, getButtonProps } = useDoubleCheck()\n\n\treturn (\n\t\t<AdminButton\n\t\t\t{...getButtonProps(props)}\n\t\t\tclassName={cn(\n\t\t\t\tprops.className,\n\t\t\t\tdoubleCheck\n\t\t\t\t\t? (doubleCheckClassName ??\n\t\t\t\t\t\t\t'border-destructive bg-destructive text-destructive-foreground')\n\t\t\t\t\t: null,\n\t\t\t)}\n\t\t>\n\t\t\t{doubleCheck ? (\n\t\t\t\t<>\n\t\t\t\t\t<Icon name=\"TriangleAlert\" className=\"h-4 w-4\" />\n\t\t\t\t\tAre you sure?\n\t\t\t\t</>\n\t\t\t) : (\n\t\t\t\tchildren\n\t\t\t)}\n\t\t</AdminButton>\n\t)\n}\n\nfunction Pinger({\n\tstatus,\n}: {\n\tstatus: 'running' | 'starting' | 'stopped' | 'taken'\n}) {\n\tconst colors = {\n\t\trunning: {\n\t\t\tpinger: 'bg-success/60',\n\t\t\tcircle: 'bg-success',\n\t\t},\n\t\tstarting: {\n\t\t\tpinger: 'bg-info/60',\n\t\t\tcircle: 'bg-info',\n\t\t},\n\t\tstopped: {\n\t\t\tcircle: 'bg-muted-foreground',\n\t\t},\n\t\ttaken: {\n\t\t\tpinger: 'bg-destructive/60',\n\t\t\tcircle: 'bg-destructive',\n\t\t},\n\t}[status]\n\treturn (\n\t\t<span className=\"relative flex h-3 w-3 shrink-0\">\n\t\t\t{colors.pinger ? (\n\t\t\t\t<span\n\t\t\t\t\tclassName={clsx(\n\t\t\t\t\t\t'absolute inline-flex h-full w-full animate-ping rounded-full opacity-75',\n\t\t\t\t\t\tcolors.pinger,\n\t\t\t\t\t)}\n\t\t\t\t/>\n\t\t\t) : null}\n\t\t\t<span\n\t\t\t\tclassName={clsx(\n\t\t\t\t\t'relative inline-flex h-3 w-3 rounded-full',\n\t\t\t\t\tcolors.circle,\n\t\t\t\t)}\n\t\t\t/>\n\t\t</span>\n\t)\n}\n"],"names":["handle","getSitemapEntries","meta","matches","title","getRootMatchLoaderData","workshopTitle","sortProgress","a","b","type","linkProgress","progress","exerciseNumber","toString","padStart","stepNumber","index","_UNSAFE_withComponentProps","loaderData","data","navigation","useNavigation","epicProgress","useEpicProgress","isStartingInspector","formData","get","isStoppingInspector","progressStatus","completed","incomplete","jsxs","className","children","jsx","Object","entries","sidecarProcesses","length","Card","CardHeader","CardTitle","CardDescription","CardContent","map","key","process","SidecarProcessItem","name","pid","running","Form","method","DoubleCheckAdminButton","value","Icon","doubleCheckClassName","inspectorRunning","AdminButton","apps","app","processes","Pinger","status","port","style","backgroundColor","color","testProcesses","exitCode","sort","epicCompletedAt","label","epicLessonSlug","filter","Boolean","join","clsx","SimpleTooltip","content","Link","to","epicLessonUrl","restartFetcher","useFetcher","logsFetcher","copyStatus","setCopyStatus","React","logsOpen","setLogsOpen","displayLogs","setDisplayLogs","isRestarting","state","isLoadingLogs","logs","handleCopyLogs","navigator","clipboard","writeText","then","setTimeout","catch","handleToggleLogs","open","submit","intent","disabled","cn","onToggle","e","currentTarget","onClick","props","doubleCheck","getButtonProps","useDoubleCheck","Fragment","colors","pinger","circle","starting","stopped","taken"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{w as s,a,b as i,L as l}from"./chunk-EPOLDU6W-BCLmut3y.js";import{j as e}from"./jsx-runtime-C5WNSv3b.js";import{E as n}from"./index-CEVyDj51.js";import{E as m}from"./epic-video-DnXXh6qE.js";import{G as d}from"./error-boundary-
|
|
2
|
-
//# sourceMappingURL=index-
|
|
1
|
+
import{w as s,a,b as i,L as l}from"./chunk-EPOLDU6W-BCLmut3y.js";import{j as e}from"./jsx-runtime-C5WNSv3b.js";import{E as n}from"./index-CEVyDj51.js";import{E as m}from"./epic-video-DnXXh6qE.js";import{G as d}from"./error-boundary-DxHqAEHX.js";import{u as c}from"./revalidation-ws-BJWJviUX.js";import{E as x}from"./launch-editor-D2exGfVu.js";import{M as p}from"./mdx-Dun3ONG_.js";import{c as f}from"./misc-W4055b-0.js";import{g as u}from"./root-loader-BOzEMapJ.js";import{g as h}from"./seo-t5J-DRxw.js";import"./index-CqIc3cxq.js";import"./use-event-source-BuD4_2SF.js";import"./index-DzdDahau.js";import"./index-vDCSPjrM.js";import"./index-CdzVFL-Z.js";import"./tooltip-Tlsyx2YO.js";import"./pe-CIZUOJMr.js";import"./schemas-Uj5SZtvt.js";import"./online-DiNLkgTC.js";import"./loading-CDNzW5oO.js";import"./user-BsPobzjB.js";import"./workshop-config-Zfc8zU0x.js";import"./preload-helper-BXl3LOEh.js";import"./progress-bar-DpWhcyhC.js";const Q=o=>{const r=o.data,t=u(o.matches);return!r||!t?[{title:"🦉 | Error"}]:h({title:`📚 | ${r.title} | ${t.workshopTitle}`,description:`Extras for ${t.workshopTitle}`,ogTitle:r.title,ogDescription:`Extras for ${t.workshopTitle}`,instructor:t.instructor,requestInfo:t.requestInfo})};function j({extra:o}){return e.jsx("li",{children:e.jsxs(l,{className:f("relative flex items-center gap-4 px-4 py-3 text-lg font-semibold transition","hover:bg-muted/60 focus:bg-muted/60"),prefetch:"intent",to:o.dirName,children:[e.jsx("span",{className:"text-muted-foreground text-xs font-normal tabular-nums",children:"•"}),e.jsx("span",{className:"truncate",children:o.title})]})})}const v={h1:()=>null},W=s(function(){const r=i();return c({watchPaths:["./extra","./example","./examples"]}),e.jsxs("main",{className:"relative flex h-full w-full max-w-5xl flex-col justify-between border-r md:w-3/4 xl:w-2/3",children:[e.jsxs("article",{id:r.articleId,className:"shadow-on-scrollbox scrollbar-thin scrollbar-thumb-scrollbar flex w-full flex-1 flex-col gap-12 overflow-y-scroll px-3 py-4 pt-6 md:px-10 md:py-12 md:pt-16",children:[e.jsx("div",{children:e.jsx("h1",{className:"text-[clamp(3rem,6vw,7.5rem)] leading-none font-extrabold",children:r.title})}),e.jsx("div",{children:r.extrasReadme.compiled.status==="success"&&r.extrasReadme.compiled.code?e.jsx(m,{epicVideoInfosPromise:r.epicVideoInfosPromise,children:e.jsx("div",{className:"prose dark:prose-invert sm:prose-lg",children:e.jsx(p,{code:r.extrasReadme.compiled.code,components:v})})}):r.extrasReadme.compiled.status==="error"?e.jsxs("div",{className:"text-foreground-destructive",children:["There was an error:",e.jsx("pre",{children:r.extrasReadme.compiled.error})]}):"No extras overview yet..."}),e.jsxs("div",{className:"pt-6",children:[e.jsx("h2",{className:"pb-4 font-mono text-xs font-semibold uppercase",children:"Extras"}),r.extras.length?e.jsx("ul",{className:"divide-border dark:divide-border/50 flex flex-col divide-y",children:r.extras.map(t=>e.jsx(j,{extra:t},t.dirName))}):e.jsx("p",{className:"text-muted-foreground",children:"No extras yet. Add one to get started."})]})]}),e.jsx(n,{elementQuery:`#${r.articleId}`}),e.jsx("div",{className:"@container flex h-16 justify-center border-t",children:e.jsx(x,{file:r.extrasReadme.file,relativePath:r.extrasReadme.relativePath})})]})}),z=a(function(){return e.jsx(d,{})});export{z as ErrorBoundary,W as default,Q as meta};
|
|
2
|
+
//# sourceMappingURL=index-IgBUhsxX.js.map
|