@realiizlabs/admin 0.14.1 → 0.15.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.
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { createContext, useContext, useState, useId, useTransition, useEffect, Suspense, useMemo, useRef, useCallback, useSyncExternalStore, Fragment as Fragment$1 } from 'react';
2
+ import { createContext, useContext, useState, useEffect, useId, useTransition, Suspense, useMemo, useRef, useCallback, useSyncExternalStore, Fragment as Fragment$1 } from 'react';
3
3
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
4
4
  import { usePathname, useRouter, useSearchParams } from 'next/navigation';
5
5
  import { createBrowserClient } from '@supabase/ssr';
@@ -888,12 +888,149 @@ var STUDIO_CSS = `
888
888
  .rz-admin .rz-help__pager a:hover { color: var(--signal); }
889
889
  .rz-admin .rz-arrow--back { transform: none; }
890
890
  @media (prefers-reduced-motion: no-preference) { .rz-admin a:hover .rz-arrow--back { transform: translateX(-4px); } }
891
+
892
+ /* ---- Studio update strip / Settings \u2192 Studio ---------------------------- */
893
+ .rz-admin .rz-update { border: 1px solid var(--border); border-left: 3px solid var(--signal); border-radius: var(--rz-radius); background: var(--surface-1); padding: .7rem 1rem; }
894
+ .rz-admin .rz-update--strip { margin: 0 0 1.25rem; }
895
+ .rz-admin .rz-update__row { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem 1rem; }
896
+ .rz-admin .rz-update__title { font-weight: 600; color: var(--text-primary); }
897
+ .rz-admin .rz-update__link { display: inline-flex; align-items: center; gap: .3rem; background: none; border: 0; padding: 0; font: inherit; font-size: .875rem; color: var(--signal); cursor: pointer; text-decoration: none; }
898
+ .rz-admin .rz-update__link:hover { color: var(--text-primary); }
899
+ .rz-admin .rz-update__later { margin-left: auto; background: none; border: 0; padding: 0; font: inherit; font-size: .8rem; color: var(--text-muted); cursor: pointer; }
900
+ .rz-admin .rz-update__later:hover { color: var(--text-primary); }
901
+ .rz-admin .rz-update__notes { margin: .6rem 0 0; font-size: .875rem; line-height: 1.55; color: var(--text-secondary); white-space: pre-line; }
902
+ .rz-admin .rz-update__problem { margin: .6rem 0 0; font-size: .875rem; color: var(--status-down); }
903
+ .rz-admin .rz-update__done { display: block; margin-top: .5rem; font-size: .875rem; color: var(--text-secondary); }
891
904
  `;
892
- function StudioShellFrame({ siteName, logoUrl, siteUrl, user, role, nav, pinnedNav, branding, children }) {
905
+ var POLL_MS = 8e3;
906
+ var LOCAL_FALLBACK_MS = 15e4;
907
+ var GIVE_UP_MS = 15 * 6e4;
908
+ function useDeployed(active, mergedSha) {
909
+ const [state, setState] = useState("waiting");
910
+ useEffect(() => {
911
+ if (!active) return;
912
+ let stop = false;
913
+ let baseline;
914
+ const started = Date.now();
915
+ const read2 = async () => {
916
+ try {
917
+ const r = await fetch("/api/version", { cache: "no-store" });
918
+ const j = await r.json();
919
+ return j.sha;
920
+ } catch {
921
+ return null;
922
+ }
923
+ };
924
+ const tick = async () => {
925
+ if (stop) return;
926
+ const sha = await read2();
927
+ if (stop) return;
928
+ if (baseline === void 0) baseline = sha;
929
+ const elapsed = Date.now() - started;
930
+ const matched = sha !== null && mergedSha !== null && sha === mergedSha;
931
+ const moved = sha !== null && baseline !== null && sha !== baseline;
932
+ const localDone = sha === null && elapsed >= LOCAL_FALLBACK_MS;
933
+ if (matched || moved || localDone) {
934
+ setState("live");
935
+ return;
936
+ }
937
+ if (elapsed >= GIVE_UP_MS) {
938
+ setState("slow");
939
+ return;
940
+ }
941
+ setTimeout(tick, POLL_MS);
942
+ };
943
+ void tick();
944
+ return () => {
945
+ stop = true;
946
+ };
947
+ }, [active, mergedSha]);
948
+ return state;
949
+ }
950
+ var HIDE_KEY = "rz-update-later";
951
+ function canSeeUpdates(role) {
952
+ return role === "owner" || role === "staff";
953
+ }
954
+ function UpdateStrip({ update, role }) {
955
+ const [hidden, setHidden] = useState(true);
956
+ useEffect(() => {
957
+ try {
958
+ setHidden(sessionStorage.getItem(HIDE_KEY) === update?.version);
959
+ } catch {
960
+ setHidden(false);
961
+ }
962
+ }, [update?.version]);
963
+ if (!update || update.state !== "ready" || !canSeeUpdates(role) || hidden) return null;
964
+ const later = () => {
965
+ try {
966
+ sessionStorage.setItem(HIDE_KEY, update.version);
967
+ } catch {
968
+ }
969
+ setHidden(true);
970
+ };
971
+ return /* @__PURE__ */ jsx(UpdateBody, { update, onLater: later, compact: true });
972
+ }
973
+ function UpdateBody({ update, onLater, compact = false }) {
974
+ const { actions } = useStudio();
975
+ const [showNotes, setShowNotes] = useState(!compact);
976
+ const [busy, setBusy] = useState(false);
977
+ const [outcome, setOutcome] = useState(null);
978
+ const merged = outcome?.state === "merged";
979
+ const deploy = useDeployed(merged, merged ? outcome.sha : null);
980
+ const run = async () => {
981
+ setBusy(true);
982
+ try {
983
+ setOutcome(await actions.updateStudio(update.number));
984
+ } finally {
985
+ setBusy(false);
986
+ }
987
+ };
988
+ if (merged) {
989
+ const live = deploy === "live";
990
+ const steps = [
991
+ { label: "Updated", state: "done", note: `Studio ${update.version}` },
992
+ { label: "Live", state: live ? "done" : "active", note: live ? "On the site now" : deploy === "slow" ? "Taking longer than usual" : "Rebuilding, about two minutes" }
993
+ ];
994
+ return /* @__PURE__ */ jsxs("div", { className: `rz-update${compact ? " rz-update--strip" : ""}`, role: "status", children: [
995
+ /* @__PURE__ */ jsx(Steps, { steps }),
996
+ live && /* @__PURE__ */ jsxs("span", { className: "rz-update__done", children: [
997
+ "Studio updated to ",
998
+ update.version,
999
+ ". Reload to see it."
1000
+ ] })
1001
+ ] });
1002
+ }
1003
+ const problem = outcome && outcome.state !== "pending" ? outcome.state === "stale" ? "The site changed since the checks ran. Try again in a minute." : outcome.state === "red" ? `The checks failed${outcome.failingCheck ? ` (${outcome.failingCheck})` : ""}. Your web team has been able to see this.` : outcome.state === "error" ? outcome.message : null : null;
1004
+ return /* @__PURE__ */ jsxs("div", { className: `rz-update${compact ? " rz-update--strip" : ""}`, role: "status", children: [
1005
+ /* @__PURE__ */ jsxs("div", { className: "rz-update__row", children: [
1006
+ /* @__PURE__ */ jsxs("span", { className: "rz-update__title", children: [
1007
+ "Studio ",
1008
+ update.version,
1009
+ " is available"
1010
+ ] }),
1011
+ /* @__PURE__ */ jsx("button", { type: "button", className: "rz-update__link", onClick: () => setShowNotes((v) => !v), "aria-expanded": showNotes, children: "What\u2019s new" }),
1012
+ update.previewUrl && /* @__PURE__ */ jsxs("a", { href: update.previewUrl, target: "_blank", rel: "noreferrer", className: "rz-update__link", children: [
1013
+ "Preview ",
1014
+ /* @__PURE__ */ jsx(OutboundArrow, {})
1015
+ ] }),
1016
+ /* @__PURE__ */ jsx(Button, { size: "sm", onClick: run, disabled: busy, children: busy ? "Updating\u2026" : "Update" }),
1017
+ onLater && /* @__PURE__ */ jsx("button", { type: "button", className: "rz-update__later", onClick: onLater, children: "Later" })
1018
+ ] }),
1019
+ showNotes && /* @__PURE__ */ jsx("p", { className: "rz-update__notes", children: update.notes ?? `Version ${update.version} of Studio, replacing ${update.currentVersion}. Nothing changes on your website until you press Update.` }),
1020
+ problem && /* @__PURE__ */ jsx("p", { className: "rz-update__problem", children: problem })
1021
+ ] });
1022
+ }
1023
+ function StudioShellFrame({ siteName, logoUrl, siteUrl = "/", user, role, nav, pinnedNav, branding, update = null, children }) {
893
1024
  const pathname = usePathname() ?? "/admin";
1025
+ useEffect(() => {
1026
+ if (user) document.cookie = "rz-studio=1; Path=/; Max-Age=2592000; SameSite=Lax";
1027
+ }, [user]);
894
1028
  return /* @__PURE__ */ jsxs(Fragment, { children: [
895
1029
  /* @__PURE__ */ jsx("style", { children: STUDIO_CSS }),
896
- /* @__PURE__ */ jsx(AdminShell, { siteName, logoUrl: logoUrl ?? void 0, siteUrl, user, role, nav, pinnedNav, branding, accountHref: null, activeHref: pathname, children })
1030
+ /* @__PURE__ */ jsxs(AdminShell, { siteName, logoUrl: logoUrl ?? void 0, siteUrl, user, role, nav, pinnedNav, branding, accountHref: null, activeHref: pathname, children: [
1031
+ /* @__PURE__ */ jsx(UpdateStrip, { update, role }),
1032
+ children
1033
+ ] })
897
1034
  ] });
898
1035
  }
899
1036
 
@@ -2905,51 +3042,6 @@ function RemoveItem({ typeId, slug }) {
2905
3042
  error && /* @__PURE__ */ jsx("div", { className: "rz-alert admin__alert", children: error })
2906
3043
  ] });
2907
3044
  }
2908
- var POLL_MS = 8e3;
2909
- var LOCAL_FALLBACK_MS = 15e4;
2910
- var GIVE_UP_MS = 15 * 6e4;
2911
- function useDeployed(active, mergedSha) {
2912
- const [state, setState] = useState("waiting");
2913
- useEffect(() => {
2914
- if (!active) return;
2915
- let stop = false;
2916
- let baseline;
2917
- const started = Date.now();
2918
- const read2 = async () => {
2919
- try {
2920
- const r = await fetch("/api/version", { cache: "no-store" });
2921
- const j = await r.json();
2922
- return j.sha;
2923
- } catch {
2924
- return null;
2925
- }
2926
- };
2927
- const tick = async () => {
2928
- if (stop) return;
2929
- const sha = await read2();
2930
- if (stop) return;
2931
- if (baseline === void 0) baseline = sha;
2932
- const elapsed = Date.now() - started;
2933
- const matched = sha !== null && mergedSha !== null && sha === mergedSha;
2934
- const moved = sha !== null && baseline !== null && sha !== baseline;
2935
- const localDone = sha === null && elapsed >= LOCAL_FALLBACK_MS;
2936
- if (matched || moved || localDone) {
2937
- setState("live");
2938
- return;
2939
- }
2940
- if (elapsed >= GIVE_UP_MS) {
2941
- setState("slow");
2942
- return;
2943
- }
2944
- setTimeout(tick, POLL_MS);
2945
- };
2946
- void tick();
2947
- return () => {
2948
- stop = true;
2949
- };
2950
- }, [active, mergedSha]);
2951
- return state;
2952
- }
2953
3045
  var REFRESH_MS = 1e4;
2954
3046
  function PublishPanel({ number, htmlUrl, merged, closed = false, branch, removal = false, mergedSha = null, status, item }) {
2955
3047
  const { mergeContentPr, discardChange } = useStudio().actions;
@@ -3848,12 +3940,46 @@ function BusinessTab({ initial }) {
3848
3940
  ] })
3849
3941
  ] });
3850
3942
  }
3851
- var LABELS = { profile: "My Profile", password: "Change Password", team: "Team", business: "Business" };
3852
- function AccountTabs({ me, manages, team, branding, myUserId, myRole }) {
3943
+ function StudioTab({ update, version, role }) {
3944
+ const { supportName } = useStudio();
3945
+ return /* @__PURE__ */ jsxs("div", { className: "acct-stack", children: [
3946
+ /* @__PURE__ */ jsxs(Card, { className: "acct-card", children: [
3947
+ /* @__PURE__ */ jsxs("h2", { className: "acct-title", children: [
3948
+ "Studio ",
3949
+ version
3950
+ ] }),
3951
+ !update && /* @__PURE__ */ jsx("p", { className: "acct-blurb", children: "You\u2019re up to date. When a new version comes out, it appears here and as a strip at the top of every Studio page." }),
3952
+ update?.state === "checking" && /* @__PURE__ */ jsxs("p", { className: "acct-blurb", children: [
3953
+ "Studio ",
3954
+ update.version,
3955
+ " is on its way \u2014 the checks are still running. Come back in a couple of minutes."
3956
+ ] }),
3957
+ update?.state === "failed" && /* @__PURE__ */ jsxs("p", { className: "acct-blurb", children: [
3958
+ "Studio ",
3959
+ update.version,
3960
+ " is available, but its checks failed, so it can\u2019t be installed yet.",
3961
+ role === "staff" && update.failingCheck ? ` Failing check: ${update.failingCheck}.` : ` ${supportName} has been able to see this and will sort it out.`
3962
+ ] }),
3963
+ update?.state === "ready" && /* @__PURE__ */ jsx(UpdateBody, { update })
3964
+ ] }),
3965
+ /* @__PURE__ */ jsxs(Card, { className: "acct-card", children: [
3966
+ /* @__PURE__ */ jsx("h2", { className: "acct-title", children: "How updates work" }),
3967
+ /* @__PURE__ */ jsxs("p", { className: "acct-blurb", children: [
3968
+ "Studio is a small package inside your website. When ",
3969
+ supportName,
3970
+ " releases a new version, your site prepares the update as a checked change \u2014 the same way a blog post is checked before it goes live \u2014 and builds a private preview. Nothing changes until you press ",
3971
+ /* @__PURE__ */ jsx("b", { children: "Update" }),
3972
+ ". If the update would break anything, the button never unlocks."
3973
+ ] })
3974
+ ] })
3975
+ ] });
3976
+ }
3977
+ var LABELS = { profile: "My Profile", password: "Change Password", team: "Team", business: "Business", studio: "Studio" };
3978
+ function AccountTabs({ me, manages, team, branding, myUserId, myRole, update = null, version = "" }) {
3853
3979
  const router = useRouter();
3854
3980
  const pathname = usePathname();
3855
3981
  const params = useSearchParams();
3856
- const ids = manages ? ["profile", "password", "team", "business"] : ["profile", "password"];
3982
+ const ids = manages ? ["profile", "password", "team", "business", "studio"] : ["profile", "password"];
3857
3983
  const requested = params.get("tab");
3858
3984
  const active = requested && ids.includes(requested) ? requested : "profile";
3859
3985
  const setActive = (v) => router.replace(`${pathname}?tab=${v}`, { scroll: false });
@@ -3862,14 +3988,15 @@ function AccountTabs({ me, manages, team, branding, myUserId, myRole }) {
3862
3988
  active === "profile" && /* @__PURE__ */ jsx(ProfileTab, { email: me.email, name: me.name, displayName: me.displayName, avatarUrl: me.avatarUrl }),
3863
3989
  active === "password" && /* @__PURE__ */ jsx(PasswordTab, {}),
3864
3990
  active === "team" && manages && (team?.ok ? /* @__PURE__ */ jsx(TeamTab, { members: team.members, canInvite: team.canInvite, myUserId, myRole }) : /* @__PURE__ */ jsx("div", { className: "rz-alert admin__alert", children: team?.error ?? "Couldn\u2019t load the team." })),
3865
- active === "business" && manages && /* @__PURE__ */ jsx(BusinessTab, { initial: branding })
3991
+ active === "business" && manages && /* @__PURE__ */ jsx(BusinessTab, { initial: branding }),
3992
+ active === "studio" && manages && /* @__PURE__ */ jsx(StudioTab, { update, version, role: myRole })
3866
3993
  ] });
3867
3994
  }
3868
- function StudioAccount({ me, manages, team, branding, myUserId, myRole }) {
3995
+ function StudioAccount({ me, manages, team, branding, myUserId, myRole, update = null, version = "" }) {
3869
3996
  return /* @__PURE__ */ jsxs("div", { className: "acct", children: [
3870
3997
  /* @__PURE__ */ jsx("h1", { className: "acct-h1", children: "Account" }),
3871
3998
  /* @__PURE__ */ jsx("p", { className: "acct-email", children: me.email }),
3872
- /* @__PURE__ */ jsx(Suspense, { fallback: null, children: /* @__PURE__ */ jsx(AccountTabs, { me, manages, team, branding, myUserId, myRole }) })
3999
+ /* @__PURE__ */ jsx(Suspense, { fallback: null, children: /* @__PURE__ */ jsx(AccountTabs, { me, manages, team, branding, myUserId, myRole, update, version }) })
3873
4000
  ] });
3874
4001
  }
3875
4002
 
@@ -4007,6 +4134,28 @@ The status becomes **Needs fixing** and the message names the problem. Open the
4007
4134
  | **Removing\u2026** / **Removal ready** | You asked to take an item down; same steps as any change. | Publish to make the removal live, or discard to keep the item. |
4008
4135
 
4009
4136
  One item has at most one change waiting at a time. Saving again updates that same change rather than starting a second one.
4137
+ `
4138
+ },
4139
+ {
4140
+ id: "studio-pill",
4141
+ section: "start",
4142
+ title: "Getting back to Studio from the website",
4143
+ summary: "A small Studio pill appears bottom-left on the website while you're signed in \u2014 Edit this page, or Dashboard.",
4144
+ keywords: ["pill", "edit this page", "admin bar", "back to studio", "website"],
4145
+ body: `
4146
+ ## What it is
4147
+
4148
+ While you're signed in to Studio, your public website shows a small dark pill in the bottom-left corner: **Studio \xB7 Edit this page \xB7 Dashboard**. Visitors never see it \u2014 it only appears in a browser where you've signed in.
4149
+
4150
+ ## Why it's useful
4151
+
4152
+ Spot a typo while reading your own site? **Edit this page** opens that exact post or event in Studio. **Dashboard** takes you to the Studio home.
4153
+
4154
+ ## Good to know
4155
+
4156
+ - **Edit this page** only shows on pages Studio manages (a blog post, an event). On the home page or About, you get **Dashboard** only.
4157
+ - The \xD7 hides the pill for that browser tab; it comes back next time you open the site.
4158
+ - It disappears when you sign out of Studio. If you see it but Studio asks you to sign in again, your session simply expired \u2014 sign in and carry on.
4010
4159
  `
4011
4160
  }
4012
4161
  ];
@@ -4827,6 +4976,38 @@ Pictures save the moment you choose them (**Choose a picture**, **Replace**, **R
4827
4976
  ## Where the pictures live
4828
4977
 
4829
4978
  In Studio's own secure storage \u2014 not in your website's files, so they don't go through Save/Publish.
4979
+ `
4980
+ },
4981
+ {
4982
+ id: "updating-studio",
4983
+ section: "account",
4984
+ title: "Updating Studio",
4985
+ summary: "When a new version is out, owners see a strip with what's new, a preview and an Update button. Nothing installs until you press it.",
4986
+ keywords: ["update", "upgrade", "new version", "release", "what's new", "studio version"],
4987
+ body: `
4988
+ ## What it is
4989
+
4990
+ Studio is a small package inside your website, and it improves over time. When {supportName} releases a new version, your website prepares the update automatically as a **checked change** \u2014 exactly like a blog post is checked before it goes live \u2014 and builds a private preview of your site with the new Studio in it.
4991
+
4992
+ ## Where you see it
4993
+
4994
+ - A quiet strip at the top of every Studio page: **Studio 0.15 is available \xB7 What's new \xB7 Preview \xB7 Update \xB7 Later**. Only owners and {supportName} see it; editors don't.
4995
+ - **Settings \u2192 Studio**: the version you're running, the pending update with its notes, and the same Update button.
4996
+
4997
+ The strip appears only once the update's checks have passed. If they fail, nothing is offered \u2014 {supportName} sees the failure and fixes it.
4998
+
4999
+ ## How to update
5000
+
5001
+ 1. Read **What's new** (a few lines, plain English).
5002
+ 2. Optional: press **Preview** to open your website running the new Studio.
5003
+ 3. Press **Update**. The tracker shows *Updated \u2192 Live*; about two minutes later the website has rebuilt.
5004
+ 4. Reload Studio to use the new version.
5005
+
5006
+ ## Good to know
5007
+
5008
+ - **Later** hides the strip for this browser tab; it comes back next time.
5009
+ - Nothing on your public website changes because of a Studio update \u2014 only the dashboard does.
5010
+ - You can't skip a version or go back from here; if something looks wrong after an update, tell {supportName}.
4830
5011
  `
4831
5012
  }
4832
5013
  ];
@@ -5265,8 +5446,12 @@ function BackArrow() {
5265
5446
  ] });
5266
5447
  }
5267
5448
 
5449
+ // src/version.ts
5450
+ var ADMIN_VERSION = "0.15.1" ;
5451
+
5268
5452
  // src/studio-ui/types.ts
5269
5453
  var ACTION_NAMES = [
5454
+ "updateStudio",
5270
5455
  "publishContent",
5271
5456
  "removeContent",
5272
5457
  "mergeContentPr",
@@ -5286,6 +5471,6 @@ var ACTION_NAMES = [
5286
5471
  "remove"
5287
5472
  ];
5288
5473
 
5289
- export { ACTION_NAMES, ContentEditor, GLOSSARY, HELP_ARTICLES, HELP_SECTIONS, Jargon, PublishPanel, RemoveItem, STUDIO_CSS, STUDIO_FACTS, StudioAcceptInvite, StudioAccount, StudioDashboard, StudioEdit, StudioHelp, StudioList, StudioNew, StudioNoAccess, StudioNotConfigured, StudioProvider, StudioPullRequest, StudioShellFrame, StudioSignIn, StudioTerm, copyFor, fillVars, helpAsText, joinTypes, publicDirOf, searchHelp, useEntry, useStudio };
5474
+ export { ACTION_NAMES, ADMIN_VERSION, ContentEditor, GLOSSARY, HELP_ARTICLES, HELP_SECTIONS, Jargon, PublishPanel, RemoveItem, STUDIO_CSS, STUDIO_FACTS, StudioAcceptInvite, StudioAccount, StudioDashboard, StudioEdit, StudioHelp, StudioList, StudioNew, StudioNoAccess, StudioNotConfigured, StudioProvider, StudioPullRequest, StudioShellFrame, StudioSignIn, StudioTerm, UpdateBody, UpdateStrip, canSeeUpdates, copyFor, fillVars, helpAsText, joinTypes, publicDirOf, searchHelp, useEntry, useStudio };
5290
5475
  //# sourceMappingURL=index.js.map
5291
5476
  //# sourceMappingURL=index.js.map