Skip to content

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

Commit

Fixes from reviewing the per-card commits. An issue's Save assignees cleared every assignee when the workspace's people could not be read (74236dee showed Unavailable in place of the picker but kept the button, so the form sent no one): the picker stays, holding who is assigned now, with the note above it, as the pull request page already did. A large repository's About sidebar said its releases, contributors, languages and packages could not be loaded whenever they took longer than the page waits (4 s), which is slow, not failed: it is left out quietly again, as before, and only a service that did not answer or answered with an error says so. Raise my limit and Tell g1t offered their forms when the requests could not be read, so a second request could go while one was open (not new: the page read none before): each card now takes the failed requests and shows Unavailable under its heading instead of its form. Checked: apps/web tsc -b; npm test (987).

G1t-Session:
ses_01m4zzpc96ect8d41mffgr1a45
syntaqxcommitted Parentf846f20Browse files
4 files+44−280/4 viewed
+32−12
2222 spendRange,
2323 } from "../lib/billing";
2424 import { money, wholeDollars } from "../lib/money";
25+import { type Failed, isFailed } from "../lib/partial";
26+import { Unavailable } from "./boundary";
2527 import { ErrorText, SubmitButton } from "./ui";
2628 import { Badge } from "./ui/badge";
2729 import { RadioGroup, RadioGroupItem } from "./ui/radio-group";
359361 );
360362 }
361363
362−export function RaiseCard({ requests, owner, error }: { requests: LimitRequest[]; owner: boolean; error?: string }) {
364+export function RaiseCard({ requests, owner, error }: { requests: LimitRequest[] | Failed; owner: boolean; error?: string }) {
365+ const about =
366+ "Ask for a higher limit than you can set yourself. A person at g1t reads it and answers within one business day, in the app and by email. Nothing about price is negotiated.";
367+ // Without the requests there is no telling whether one is open: no form,
368+ // so a second one is never sent while the first waits.
369+ if (isFailed(requests)) {
370+ return (
371+ <Card id="raise" title="Raise my limit" about={about}>
372+ <div className="mt-3">
373+ <Unavailable what={requests.what} compact />
374+ </div>
375+ </Card>
376+ );
377+ }
363378 const mine = requests.filter((request) => request.kind === "limit");
364379 const waiting = mine.some((request) => request.status === "open");
365380 return (
366− <Card
367− id="raise"
368− title="Raise my limit"
369− about="Ask for a higher limit than you can set yourself. A person at g1t reads it and answers within one business day, in the app and by email. Nothing about price is negotiated."
370− >
381+ <Card id="raise" title="Raise my limit" about={about}>
371382 {owner && !waiting && (
372383 <Form method="post" className="mt-4 space-y-3">
373384 <input type="hidden" name="intent" value="request" />
512523
513524 // --- Overage -----------------------------------------------------------------------
514525
515−export function OverageCard({ requests, owner, error }: { requests: LimitRequest[]; owner: boolean; error?: string }) {
526+export function OverageCard({ requests, owner, error }: { requests: LimitRequest[] | Failed; owner: boolean; error?: string }) {
527+ const title = "Spent more than you meant to? Tell us.";
528+ const about =
529+ "A loop that ran all night, a workflow on every push: say what happened. g1t can credit usage you did not mean, once in 12 months; larger credits, or a second one within 12 months, are reviewed by a person.";
530+ // As on Raise my limit: no form while an open request may be unseen.
531+ if (isFailed(requests)) {
532+ return (
533+ <Card id="overage" title={title} about={about}>
534+ <div className="mt-3">
535+ <Unavailable what={requests.what} compact />
536+ </div>
537+ </Card>
538+ );
539+ }
516540 const mine = requests.filter((request) => request.kind === "overage");
517541 const waiting = mine.some((request) => request.status === "open");
518542 return (
519− <Card
520− id="overage"
521− title="Spent more than you meant to? Tell us."
522− about="A loop that ran all night, a workflow on every push: say what happened. g1t can credit usage you did not mean, once in 12 months; larger credits, or a second one within 12 months, are reviewed by a person."
523− >
543+ <Card id="overage" title={title} about={about}>
524544 {owner && !waiting && (
525545 <Form method="post" className="mt-4 space-y-3">
526546 <input type="hidden" name="intent" value="request" />
+5−2
2121 }
2222
2323 /** Like `within`, but one that fails or does not answer in time is `Failed`, for its section to say so. */
24−function partWithin<T>(promise: Promise<T>, what: string): Promise<T | Failed> {
25− const late = new Promise<never>((_, reject) => setTimeout(() => reject(new Error(`no answer in ${WAIT_MS} ms`)), WAIT_MS));
24+function partWithin<T>(promise: Promise<T>, what: string): Promise<T | Failed | null> {
25+ // Slow is not failed: a large repository's About can take longer than
26+ // the page waits, and then it is quietly left out, as it always was.
27+ // Only a service that did not answer or answered with an error says so.
28+ const late = new Promise<null>((resolve) => setTimeout(() => resolve(null), WAIT_MS));
2629 return part(Promise.race([promise, late]), what);
2730 }
2831
+4−5
677677 </summary>
678678 <Form method="post" className="mt-2 space-y-2" key={issue.assignees.join()}>
679679 <input type="hidden" name="action" value="assign" />
680− {isFailed(members) ? (
681− <Unavailable what="The workspace's people" compact />
682− ) : (
683− <PeoplePicker name="assignee" members={members} chosen={issue.assignees} />
684− )}
680+ {/* Without the workspace's people the picker still holds who is
681+ assigned now, so saving never clears them. */}
682+ {isFailed(members) && <Unavailable what="The workspace's people" compact />}
683+ <PeoplePicker name="assignee" members={isFailed(members) ? [] : members} chosen={issue.assignees} />
685684 <SubmitButton variant="outline" name="who" value="picked" pending="Saving…">
686685 Save assignees
687686 </SubmitButton>
+3−9
265265 const { slug, role, staff, account, statement, group, plan, securityPlan, entitlements, entitlementsFailed, credits, report, details, trialMicros, firstMonthMicros, notice, problem } =
266266 loaderData;
267267 const limit = orElse(loaderData.limit, null);
268− const requests = orElse(loaderData.requests, []);
269268 const { status } = account;
270269 const owner = role === "owner" || loaderData.managesBilling;
271270 const error = actionData as SectionError;
365364 <>
366365 <SpendLimitCard limit={limit} owner={owner} error={err("limit")} />
367366 <BudgetAlerts limit={limit} owner={owner} error={err("budget")} />
368− <RaiseCard requests={requests} owner={owner} error={err("raise")} />
367+ <RaiseCard requests={loaderData.requests} owner={owner} error={err("raise")} />
369368 <PrepayCard prepaidMicros={prepaid} owner={owner} live={status.live || !staff} error={err("prepay")} />
370369 </>
371370 )}
378377 credits && <CreditsCard credits={credits} />
379378 )}
380379
381− {/* Past requests are listed on the limit's cards: without them, say so rather than show none. */}
382− {isFailed(loaderData.requests) && (paying || (limit && selfServe)) && (
383− <div className="mb-6">
384− <Unavailable what={loaderData.requests.what} compact />
385− </div>
386− )}
387− {paying && <OverageCard requests={requests} owner={owner} error={err("overage")} />}
380+ {/* Each request card says so itself when the requests could not be read. */}
381+ {paying && <OverageCard requests={loaderData.requests} owner={owner} error={err("overage")} />}
388382
389383 <AddOns plan={plan} security={securityPlan} error={err("security")} />
390384

Comments

Sign in to comment.