Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 13 additions & 11 deletions app/javascript/components/Checkout/CreditCardInput.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { CardElement, Elements } from "@stripe/react-stripe-js";
import { StripeCardElement, StripeElementStyleVariant, StripeCardElementChangeEvent } from "@stripe/stripe-js";
import cx from "classnames";
import * as React from "react";

import { SavedCreditCard } from "$app/parsers/card";
Expand All @@ -9,6 +8,9 @@ import { getCssVariable } from "$app/utils/styles";

import { useFont } from "$app/components/DesignSettings";
import { Icon } from "$app/components/Icons";
import { Fieldset, FieldsetTitle } from "$app/components/ui/Fieldset";
import { InputGroup } from "$app/components/ui/InputGroup";
import { Label } from "$app/components/ui/Label";

export const CreditCardInput = ({
disabled,
Expand All @@ -32,9 +34,9 @@ export const CreditCardInput = ({
const [baseStripeStyle, setBaseStripeStyle] = React.useState<null | StripeElementStyleVariant>(null);

return (
<fieldset className={cx({ danger: invalid })}>
<legend>
<label>Card information</label>
<Fieldset state={invalid ? "danger" : undefined}>
<FieldsetTitle>
<Label>Card information</Label>
{savedCreditCard ? (
<button
className="cursor-pointer font-normal underline all-unset"
Expand All @@ -44,15 +46,15 @@ export const CreditCardInput = ({
{useSavedCard ? "Use a different card?" : "Use saved card"}
</button>
) : null}
</legend>
</FieldsetTitle>
{savedCreditCard && useSavedCard ? (
<div className="input read-only" aria-label="Saved credit card">
<InputGroup readOnly aria-label="Saved credit card">
<Icon name="outline-credit-card" />
<span>{savedCreditCard.number}</span>
<span style={{ marginLeft: "auto" }}>{savedCreditCard.expiration_date}</span>
</div>
</InputGroup>
) : (
<div className={cx("input", { disabled })} aria-label="Card information" aria-invalid={invalid}>
<InputGroup disabled={disabled} aria-label="Card information" aria-invalid={invalid}>
{baseStripeStyle == null ? (
<input
ref={(el) => {
Expand All @@ -71,7 +73,7 @@ export const CreditCardInput = ({
) : null}
<StripeElementsProvider>
<CardElement
className="fake-input"
className="flex-1"
options={{
style: { base: baseStripeStyle ?? {} },
hidePostalCode: true,
Expand All @@ -83,9 +85,9 @@ export const CreditCardInput = ({
{...(onChange ? { onChange } : {})}
/>
</StripeElementsProvider>
</div>
</InputGroup>
)}
</fieldset>
</Fieldset>
);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import * as React from "react";

import { Button } from "$app/components/Button";
import { Modal } from "$app/components/Modal";
import { Input } from "$app/components/ui/Input";
import { Label } from "$app/components/ui/Label";

type Props = {
balance: string | null;
Expand Down Expand Up @@ -38,10 +40,10 @@ export const ConfirmBalanceForfeitOnPayoutMethodChangeModal = ({ balance, open,
Please confirm that you're okay forfeiting your balance by typing <b>"I understand"</b> below and clicking{" "}
<b>Confirm</b>.
<div className="mt-4">
<label htmlFor="confirmation-input" className="sr-only">
<Label htmlFor="confirmation-input" className="sr-only">
Type "I understand" to confirm
</label>
<input
</Label>
<Input
id="confirmation-input"
type="text"
value={confirmText}
Expand Down
30 changes: 16 additions & 14 deletions app/javascript/components/CustomDomain.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import cx from "classnames";
import * as React from "react";
import { cast } from "ts-safe-cast";

import { asyncVoid } from "$app/utils/promise";
import { assertResponseError, request, ResponseError } from "$app/utils/request";

import { Button } from "$app/components/Button";
import { Fieldset, FieldsetDescription, FieldsetTitle } from "$app/components/ui/Fieldset";
import { Input } from "$app/components/ui/Input";
import { InputGroup } from "$app/components/ui/InputGroup";
import { Label } from "$app/components/ui/Label";
import { Pill } from "$app/components/ui/Pill";

type VerificationState = "initial" | "verifying" | "success" | "failure";
Expand Down Expand Up @@ -67,22 +70,21 @@ const CustomDomain = ({
});

return (
<fieldset
className={cx({
success: verificationInfo.state === "success",
danger: verificationInfo.state === "failure",
})}
<Fieldset
state={
verificationInfo.state === "success" ? "success" : verificationInfo.state === "failure" ? "danger" : undefined
}
>
<legend>
<label htmlFor={uid}>{label}</label>
<FieldsetTitle>
<Label htmlFor={uid}>{label}</Label>
{includeLearnMoreLink ? (
<a href="/help/article/153-setting-up-a-custom-domain" target="_blank" rel="noreferrer">
Learn more
</a>
) : null}
</legend>
<div className="input input-wrapper">
<input
</FieldsetTitle>
<InputGroup>
<Input
id={uid}
placeholder="yourdomain.com"
type="text"
Expand Down Expand Up @@ -112,9 +114,9 @@ const CustomDomain = ({
</Button>
</Pill>
) : null}
</div>
<small>{verificationInfo.message}</small>
</fieldset>
</InputGroup>
<FieldsetDescription>{verificationInfo.message}</FieldsetDescription>
</Fieldset>
);
};

Expand Down
28 changes: 17 additions & 11 deletions app/javascript/components/ProductEdit/ContentTab/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ import { Posts, PostsProvider } from "$app/components/TiptapExtensions/Posts";
import { ShortAnswer } from "$app/components/TiptapExtensions/ShortAnswer";
import { UpsellCard } from "$app/components/TiptapExtensions/UpsellCard";
import { Card, CardContent } from "$app/components/ui/Card";
import { Checkbox } from "$app/components/ui/Checkbox";
import { Label } from "$app/components/ui/Label";
import { Row, RowContent, Rows } from "$app/components/ui/Rows";
import { Tab, Tabs } from "$app/components/ui/Tabs";
import { Product, ProductOption, UpsellSelectModal } from "$app/components/UpsellSelectModal";
Expand Down Expand Up @@ -1064,16 +1066,18 @@ export const ContentTab = () => {
<>
<hr className="relative left-1/2 my-2 w-screen max-w-none -translate-x-1/2 border-border lg:hidden" />
<ComboBox<Variant>
// TODO: Currently needed to get the icon on the selected option even though this is not multiple select. We should fix this in the design system
multiple
input={(props) => (
<div {...props} className="input h-full min-h-auto" aria-label="Select a version">
<span className="fake-input text-singleline">
<div
{...props}
className="inline-flex h-full min-h-auto cursor-pointer items-center gap-2 rounded border border-border bg-background px-4 py-3"
aria-label="Select a version"
>
<span className="text-singleline flex-1">
{selectedVariant && !product.has_same_rich_content_for_all_variants
? `Editing: ${selectedVariant.name || "Untitled"}`
: "Editing: All versions"}
</span>
<Icon name="outline-cheveron-down" />
<Icon name="outline-cheveron-down" className="text-muted" />
</div>
)}
options={product.variants}
Expand All @@ -1088,7 +1092,7 @@ export const ContentTab = () => {
aria-selected={item.id === selectedVariantId}
inert={product.has_same_rich_content_for_all_variants}
>
<div>
<div className="flex-1">
<h4>{item.name || "Untitled"}</h4>
{item.id === selectedVariant?.id ? (
<small>Editing</small>
Expand All @@ -1109,12 +1113,14 @@ export const ContentTab = () => {
<small className="text-muted">No content yet</small>
)}
</div>
{item.id === selectedVariant?.id && (
<Icon name="solid-check-circle" className="ml-auto text-success" />
)}
</div>
{index === product.variants.length - 1 ? (
<div className="option">
<label style={{ alignItems: "center" }}>
<input
type="checkbox"
<div className="flex cursor-pointer items-center px-4 py-2">
<Label className="items-center">
<Checkbox
checked={product.has_same_rich_content_for_all_variants}
onChange={() => {
if (!product.has_same_rich_content_for_all_variants && product.variants.length > 1)
Expand All @@ -1123,7 +1129,7 @@ export const ContentTab = () => {
}}
/>
<small>Use the same content for all versions</small>
</label>
</Label>
</div>
) : null}
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { assertResponseError, request, ResponseError } from "$app/utils/request"
import { Button } from "$app/components/Button";
import { Modal } from "$app/components/Modal";
import { showAlert } from "$app/components/server-components/Alert";
import { FormSection } from "$app/components/ui/FormSection";

type Props = {
formatted_balance_to_forfeit_on_account_deletion: string | null;
Expand All @@ -32,10 +33,7 @@ const AccountDeletionSection = (props: Props) => {
};

return (
<section className="p-4! md:p-8!">
<header>
<h2>Danger Zone</h2>
</header>
<FormSection className="p-4! md:p-8!" header={<h2>Danger Zone</h2>}>
<p>
<a href="/help/article/37-how-to-delete-your-gumroad-account" target="_blank" rel="noreferrer">
Deleting your account
Expand Down Expand Up @@ -84,7 +82,7 @@ const AccountDeletionSection = (props: Props) => {
.
</p>
</Modal>
</section>
</FormSection>
);
};

Expand Down
77 changes: 40 additions & 37 deletions app/javascript/components/Settings/AdvancedPage/ApplicationForm.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { router } from "@inertiajs/react";
import { DirectUpload } from "@rails/activestorage";
import cx from "classnames";
import placeholderAppIcon from "images/gumroad_app.png";
import * as React from "react";
import { cast } from "ts-safe-cast";
Expand All @@ -12,6 +11,9 @@ import { assertResponseError, request, ResponseError } from "$app/utils/request"

import { Button } from "$app/components/Button";
import { showAlert } from "$app/components/server-components/Alert";
import { Fieldset, FieldsetTitle } from "$app/components/ui/Fieldset";
import { Input } from "$app/components/ui/Input";
import { Label } from "$app/components/ui/Label";
import { WithTooltip } from "$app/components/WithTooltip";

export type Application = {
Expand Down Expand Up @@ -126,39 +128,40 @@ const ApplicationForm = ({ application }: { application?: Application }) => {
<input
ref={iconInputRef}
type="file"
className="sr-only"
accept={ALLOWED_ICON_EXTENSIONS.map((ext) => `.${ext}`).join(",")}
tabIndex={-1}
onChange={handleIconChange}
/>
<fieldset>
<legend>
<label>Application icon</label>
</legend>
<Fieldset>
<FieldsetTitle>
<Label>Application icon</Label>
</FieldsetTitle>
<div style={{ display: "flex", gap: "var(--spacer-4)", alignItems: "flex-start" }}>
<img className="application-icon" src={icon?.url || placeholderAppIcon} width={80} height={80} />
<Button onClick={() => iconInputRef.current?.click()} disabled={isUploadingIcon || isSubmitting}>
{isUploadingIcon ? "Uploading..." : "Upload icon"}
</Button>
</div>
</fieldset>
<fieldset className={cx({ danger: name.error })}>
<legend>
<label htmlFor={`${uid}-name`}>Application name</label>
</legend>
<input
</Fieldset>
<Fieldset state={name.error ? "danger" : undefined}>
<FieldsetTitle>
<Label htmlFor={`${uid}-name`}>Application name</Label>
</FieldsetTitle>
<Input
id={`${uid}-name`}
ref={nameRef}
placeholder="Name"
type="text"
value={name.value}
onChange={(e) => setName({ value: e.target.value })}
/>
</fieldset>
<fieldset className={cx({ danger: redirectUri.error })}>
<legend>
<label htmlFor={`${uid}-redirectUri`}>Redirect URI</label>
</legend>
<input
</Fieldset>
<Fieldset state={redirectUri.error ? "danger" : undefined}>
<FieldsetTitle>
<Label htmlFor={`${uid}-redirectUri`}>Redirect URI</Label>
</FieldsetTitle>
<Input
id={`${uid}-redirectUri`}
ref={redirectUriRef}
placeholder="http://yourapp.com/callback"
Expand All @@ -167,35 +170,35 @@ const ApplicationForm = ({ application }: { application?: Application }) => {
value={redirectUri.value}
onChange={(e) => setRedirectUri({ value: e.target.value })}
/>
</fieldset>
</Fieldset>

{application ? (
<>
<fieldset>
<legend>
<label htmlFor={`${uid}-uid`}>Application ID</label>
</legend>
<input id={`${uid}-uid`} readOnly type="text" value={application.uid} />
</fieldset>
<fieldset>
<legend>
<label htmlFor={`${uid}-secret`}>Application Secret</label>
</legend>
<input id={`${uid}-secret`} readOnly type="text" value={application.secret} />
</fieldset>
<Fieldset>
<FieldsetTitle>
<Label htmlFor={`${uid}-uid`}>Application ID</Label>
</FieldsetTitle>
<Input id={`${uid}-uid`} readOnly type="text" value={application.uid} />
</Fieldset>
<Fieldset>
<FieldsetTitle>
<Label htmlFor={`${uid}-secret`}>Application Secret</Label>
</FieldsetTitle>
<Input id={`${uid}-secret`} readOnly type="text" value={application.secret} />
</Fieldset>

{token ? (
<fieldset>
<legend>
<label htmlFor={`${uid}-accessToken`}>
<Fieldset>
<FieldsetTitle>
<Label htmlFor={`${uid}-accessToken`}>
Access Token
<WithTooltip tip="This is a ready-to-use access token for our API.">
<span>(?)</span>
</WithTooltip>
</label>
</legend>
<input id={`${uid}-accessToken`} readOnly type="text" value={token} />
</fieldset>
</Label>
</FieldsetTitle>
<Input id={`${uid}-accessToken`} readOnly type="text" value={token} />
</Fieldset>
) : null}
<div className="flex gap-2">
<Button color="accent" onClick={handleSubmit} disabled={isSubmitting || isUploadingIcon}>
Expand Down
Loading