diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 0190f60a..0059aa10 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -30,7 +30,7 @@ jobs: - name: 🥟 Setup bun uses: oven-sh/setup-bun@v1 with: - bun-version: "1.3.8" + bun-version: '1.3.8' - name: 📥 Download deps run: bun install --frozen-lockfile @@ -56,7 +56,7 @@ jobs: - name: 🥟 Setup bun uses: oven-sh/setup-bun@v1 with: - bun-version: "1.3.8" + bun-version: '1.3.8' - name: 📥 Download deps run: bun install --frozen-lockfile @@ -82,7 +82,7 @@ jobs: - name: 🥟 Setup bun uses: oven-sh/setup-bun@v1 with: - bun-version: "1.3.8" + bun-version: '1.3.8' - name: 📥 Download deps run: bun install --frozen-lockfile @@ -108,7 +108,7 @@ jobs: - name: 🥟 Setup bun uses: oven-sh/setup-bun@v1 with: - bun-version: "1.3.8" + bun-version: '1.3.8' - name: 📥 Download deps run: bun install --frozen-lockfile @@ -146,7 +146,7 @@ jobs: - name: 🥟 Setup bun uses: oven-sh/setup-bun@v1 with: - bun-version: "1.3.8" + bun-version: '1.3.8' - name: 📥 Download deps run: bun install --frozen-lockfile diff --git a/app/components/search-bar.tsx b/app/components/search-bar.tsx index d043a6c9..61d5a3e7 100644 --- a/app/components/search-bar.tsx +++ b/app/components/search-bar.tsx @@ -44,17 +44,17 @@ export function SearchBar({ >
- - + Search + +
No text after 5 minutes? diff --git a/app/routes/_app+/_layout.tsx b/app/routes/_app+/_layout.tsx index 5974da3d..924b8f92 100644 --- a/app/routes/_app+/_layout.tsx +++ b/app/routes/_app+/_layout.tsx @@ -1,4 +1,4 @@ -import { useRef, useState, type CSSProperties } from 'react' +import { useRef, useState } from 'react' import { Form, Link, @@ -67,18 +67,8 @@ export default function Layout() { const data = useLoaderData() const user = useOptionalUser() const requestInfo = useRequestInfo() - const isRecipientsRoute = requestInfo.path.startsWith('/recipients') - const recipientsTheme = isRecipientsRoute - ? ({ - '--background': '0 0% 100%', - '--card': '0 0% 100%', - } as CSSProperties) - : undefined return ( -
+
-

- $14.99 -

+

$14.99

diff --git a/app/routes/_app+/recipients+/$recipientId.index.tsx b/app/routes/_app+/recipients+/$recipientId.index.tsx index 65ae038d..03a3ce45 100644 --- a/app/routes/_app+/recipients+/$recipientId.index.tsx +++ b/app/routes/_app+/recipients+/$recipientId.index.tsx @@ -605,7 +605,7 @@ export default function RecipientRoute() { {hasAnyMessages ? (
{hasPastMessages || pastNextCursor ? (
@@ -615,7 +615,7 @@ export default function RecipientRoute() {
    {pastMessagesForDisplay.map((m) => (
  • -
    +

    {m.content}

    ) : ( -
    +

    {emptyThreadMessage}

  • -
    +
    {scheduleLabel} @@ -846,7 +846,7 @@ function MessageForms({ message }: { message: FutureMessage }) { setCurrentContent(event.currentTarget.value) }} ref={textareaRef} - className="mt-2 w-full resize-none bg-transparent text-sm leading-relaxed text-[hsl(var(--palette-cream))] placeholder:text-[hsl(var(--palette-cream))]/80 focus-visible:outline-none" + className="text-message-card-foreground placeholder:text-message-card-foreground/80 mt-2 w-full resize-none bg-transparent text-sm leading-relaxed focus-visible:outline-none" rows={2} /> diff --git a/app/routes/_app+/recipients+/$recipientId.tsx b/app/routes/_app+/recipients+/$recipientId.tsx index 20ba8476..c6e99021 100644 --- a/app/routes/_app+/recipients+/$recipientId.tsx +++ b/app/routes/_app+/recipients+/$recipientId.tsx @@ -171,7 +171,7 @@ export default function RecipientRoute() {
    -
    +
    diff --git a/app/routes/_app+/recipients+/index.tsx b/app/routes/_app+/recipients+/index.tsx index 9bb41968..576c2341 100644 --- a/app/routes/_app+/recipients+/index.tsx +++ b/app/routes/_app+/recipients+/index.tsx @@ -18,7 +18,7 @@ export default function RecipientsIndexRoute() { return (
    {showTrialBanner ? ( -
    +
    Upgrade to text unlimited loved ones!{' '} Start your free trial @@ -26,7 +26,7 @@ export default function RecipientsIndexRoute() {
    ) : null} {showUpgradeBanner ? ( -
    +
    Upgrade to Premium to text more loved ones.{' '} Upgrade to Premium @@ -70,9 +70,7 @@ export default function RecipientsIndexRoute() { const messageText = `${messageCount} ${messageLabel}` const messagePreparedText = `${messageText} prepared` const messageTone = - messageCount === 0 - ? 'text-[hsl(var(--palette-orange))]' - : 'text-muted-foreground' + messageCount === 0 ? 'text-warning' : 'text-muted-foreground' const scheduleTone = recipient.disabled ? 'text-muted-foreground' : recipient.cronError @@ -122,7 +120,7 @@ export default function RecipientsIndexRoute() { {messageText} {messageCount === 0 ? ( - + ) : null} diff --git a/app/routes/_app+/settings.profile+/subscription.tsx b/app/routes/_app+/settings.profile+/subscription.tsx index 2627974f..16da79ee 100644 --- a/app/routes/_app+/settings.profile+/subscription.tsx +++ b/app/routes/_app+/settings.profile+/subscription.tsx @@ -65,9 +65,7 @@ export default function Subscribe() { 1 message per day

    -

    - $4.99 -

    +

    $4.99

    {isBasic || isPremium ? ( @@ -89,9 +87,7 @@ export default function Subscribe() { 10 messages per day

    -

    - $14.99 -

    +

    $14.99

    {isPremium ? ( diff --git a/app/styles/tailwind.css b/app/styles/tailwind.css index 63a83478..b537ad77 100644 --- a/app/styles/tailwind.css +++ b/app/styles/tailwind.css @@ -67,6 +67,50 @@ --ring: var(--palette-green-300); + --overlay: 0 0% 0%; + --inverse: var(--palette-cream); + + --brand: var(--palette-green-500); + --brand-foreground: var(--palette-cream); + --brand-soft: var(--palette-green-300); + --brand-soft-foreground: var(--palette-dark-navy); + --warm: var(--palette-chestnut); + --warm-foreground: var(--palette-cream); + + --warning: var(--palette-orange); + --warning-foreground: var(--palette-dark-navy); + + --banner-trial: var(--palette-sunny); + --banner-trial-foreground: var(--palette-dark-navy); + --banner-upgrade: var(--palette-green-300); + --banner-upgrade-foreground: var(--palette-dark-navy); + + --price-basic: var(--palette-cloud); + --price-premium: var(--palette-chestnut); + + --hero-orb: var(--palette-baby-blue); + --hero-sparkle: var(--palette-orange); + + --marketing-feature: var(--palette-green-700); + --marketing-feature-foreground: var(--palette-cream); + --marketing-feature-muted: var(--palette-green-100); + --marketing-feature-accent: var(--palette-sunny); + --marketing-feature-accent-secondary: var(--palette-orange); + --marketing-step-index: var(--palette-chestnut); + + --marketing-cta: var(--palette-dust-pink); + --marketing-cta-foreground: var(--palette-chocolate); + --marketing-cta-accent: var(--palette-chestnut); + + --message-bubble: var(--palette-green-500); + --message-bubble-foreground: var(--palette-cream); + --message-card: var(--palette-blues); + --message-card-foreground: var(--palette-cream); + + --thread-gradient-start: var(--palette-cream); + --thread-gradient-mid: var(--palette-beige); + --thread-gradient-end: 0 0% 100%; + --radius: 0.5rem; } @@ -106,6 +150,50 @@ --destructive-foreground: var(--palette-cream); --ring: var(--palette-green-300); + + --overlay: 0 0% 0%; + --inverse: var(--palette-cream); + + --brand: var(--palette-green-300); + --brand-foreground: var(--palette-dark-navy); + --brand-soft: var(--palette-green-700); + --brand-soft-foreground: var(--palette-cream); + --warm: var(--palette-orange); + --warm-foreground: var(--palette-dark-navy); + + --warning: var(--palette-sunny); + --warning-foreground: var(--palette-dark-navy); + + --banner-trial: var(--palette-orange); + --banner-trial-foreground: var(--palette-dark-navy); + --banner-upgrade: var(--palette-green-500); + --banner-upgrade-foreground: var(--palette-cream); + + --price-basic: var(--palette-sunny); + --price-premium: var(--palette-rose-pink); + + --hero-orb: var(--palette-navy); + --hero-sparkle: var(--palette-sunny); + + --marketing-feature: var(--palette-green-900); + --marketing-feature-foreground: var(--palette-cream); + --marketing-feature-muted: var(--palette-green-300); + --marketing-feature-accent: var(--palette-sunny); + --marketing-feature-accent-secondary: var(--palette-orange); + --marketing-step-index: var(--palette-sunny); + + --marketing-cta: var(--palette-navy); + --marketing-cta-foreground: var(--palette-cream); + --marketing-cta-accent: var(--palette-sunny); + + --message-bubble: var(--palette-green-500); + --message-bubble-foreground: var(--palette-cream); + --message-card: var(--palette-blues); + --message-card-foreground: var(--palette-cream); + + --thread-gradient-start: var(--palette-dark-navy); + --thread-gradient-mid: var(--palette-navy); + --thread-gradient-end: var(--palette-storm-grey); } @theme inline { @@ -137,6 +225,41 @@ --color-destructive-foreground: hsl(var(--destructive-foreground)); --color-ring: hsl(var(--ring)); --color-ring-invalid: hsl(var(--foreground-destructive)); + --color-overlay: hsl(var(--overlay)); + --color-inverse: hsl(var(--inverse)); + --color-brand: hsl(var(--brand)); + --color-brand-foreground: hsl(var(--brand-foreground)); + --color-brand-soft: hsl(var(--brand-soft)); + --color-brand-soft-foreground: hsl(var(--brand-soft-foreground)); + --color-warm: hsl(var(--warm)); + --color-warm-foreground: hsl(var(--warm-foreground)); + --color-warning: hsl(var(--warning)); + --color-warning-foreground: hsl(var(--warning-foreground)); + --color-banner-trial: hsl(var(--banner-trial)); + --color-banner-trial-foreground: hsl(var(--banner-trial-foreground)); + --color-banner-upgrade: hsl(var(--banner-upgrade)); + --color-banner-upgrade-foreground: hsl(var(--banner-upgrade-foreground)); + --color-price-basic: hsl(var(--price-basic)); + --color-price-premium: hsl(var(--price-premium)); + --color-hero-orb: hsl(var(--hero-orb)); + --color-hero-sparkle: hsl(var(--hero-sparkle)); + --color-marketing-feature: hsl(var(--marketing-feature)); + --color-marketing-feature-foreground: hsl( + var(--marketing-feature-foreground) + ); + --color-marketing-feature-muted: hsl(var(--marketing-feature-muted)); + --color-marketing-feature-accent: hsl(var(--marketing-feature-accent)); + --color-marketing-feature-accent-secondary: hsl( + var(--marketing-feature-accent-secondary) + ); + --color-marketing-step-index: hsl(var(--marketing-step-index)); + --color-marketing-cta: hsl(var(--marketing-cta)); + --color-marketing-cta-foreground: hsl(var(--marketing-cta-foreground)); + --color-marketing-cta-accent: hsl(var(--marketing-cta-accent)); + --color-message-bubble: hsl(var(--message-bubble)); + --color-message-bubble-foreground: hsl(var(--message-bubble-foreground)); + --color-message-card: hsl(var(--message-card)); + --color-message-card-foreground: hsl(var(--message-card-foreground)); } @theme { @@ -208,6 +331,17 @@ } } +@layer utilities { + .thread-gradient { + background-image: linear-gradient( + 90deg, + hsl(var(--thread-gradient-start)), + hsl(var(--thread-gradient-mid)) 45%, + hsl(var(--thread-gradient-end)) 100% + ); + } +} + @layer base { *, ::after, diff --git a/app/utils/cron-runner.server.ts b/app/utils/cron-runner.server.ts index ae2076a2..783606ae 100644 --- a/app/utils/cron-runner.server.ts +++ b/app/utils/cron-runner.server.ts @@ -3,6 +3,7 @@ import { clearIntervalAsync, setIntervalAsync, } from 'set-interval-async/dynamic' +import { getScheduleWindow } from './cron.server.ts' import { prisma } from './db.server.ts' import { getrecipientsforcron } from './prisma-generated.server/sql.ts' import { @@ -10,7 +11,6 @@ import { PREV_SCHEDULE_SENTINEL_DATE, } from './schedule-constants.server.ts' import { sendText, sendTextToRecipient } from './text.server.ts' -import { getScheduleWindow } from './cron.server.ts' const cronIntervalRef = remember<{ current: ReturnType | null diff --git a/app/utils/cron.server.test.ts b/app/utils/cron.server.test.ts index fe84610b..d09d134f 100644 --- a/app/utils/cron.server.test.ts +++ b/app/utils/cron.server.test.ts @@ -2,8 +2,8 @@ import '#tests/setup/setup-test-env.ts' import { faker } from '@faker-js/faker' import { expect, test } from 'bun:test' import { createMessage, createRecipient, createUser } from '#tests/db-utils.ts' -import { getScheduleWindow } from './cron.server.ts' import { sendNextTexts } from './cron-runner.server.ts' +import { getScheduleWindow } from './cron.server.ts' import { prisma } from './db.server.ts' test('does not send any texts if there are none to be sent', async () => { diff --git a/app/utils/extended-theme.ts b/app/utils/extended-theme.ts index 02317d67..a7c691bd 100644 --- a/app/utils/extended-theme.ts +++ b/app/utils/extended-theme.ts @@ -46,6 +46,57 @@ export const extendedTheme = { DEFAULT: 'hsl(var(--card))', foreground: 'hsl(var(--card-foreground))', }, + overlay: 'hsl(var(--overlay))', + inverse: 'hsl(var(--inverse))', + brand: { + DEFAULT: 'hsl(var(--brand))', + foreground: 'hsl(var(--brand-foreground))', + }, + 'brand-soft': { + DEFAULT: 'hsl(var(--brand-soft))', + foreground: 'hsl(var(--brand-soft-foreground))', + }, + warm: { + DEFAULT: 'hsl(var(--warm))', + foreground: 'hsl(var(--warm-foreground))', + }, + warning: { + DEFAULT: 'hsl(var(--warning))', + foreground: 'hsl(var(--warning-foreground))', + }, + 'banner-trial': { + DEFAULT: 'hsl(var(--banner-trial))', + foreground: 'hsl(var(--banner-trial-foreground))', + }, + 'banner-upgrade': { + DEFAULT: 'hsl(var(--banner-upgrade))', + foreground: 'hsl(var(--banner-upgrade-foreground))', + }, + 'price-basic': 'hsl(var(--price-basic))', + 'price-premium': 'hsl(var(--price-premium))', + 'hero-orb': 'hsl(var(--hero-orb))', + 'hero-sparkle': 'hsl(var(--hero-sparkle))', + 'marketing-feature': { + DEFAULT: 'hsl(var(--marketing-feature))', + foreground: 'hsl(var(--marketing-feature-foreground))', + muted: 'hsl(var(--marketing-feature-muted))', + accent: 'hsl(var(--marketing-feature-accent))', + 'accent-secondary': 'hsl(var(--marketing-feature-accent-secondary))', + }, + 'marketing-step-index': 'hsl(var(--marketing-step-index))', + 'marketing-cta': { + DEFAULT: 'hsl(var(--marketing-cta))', + foreground: 'hsl(var(--marketing-cta-foreground))', + accent: 'hsl(var(--marketing-cta-accent))', + }, + 'message-bubble': { + DEFAULT: 'hsl(var(--message-bubble))', + foreground: 'hsl(var(--message-bubble-foreground))', + }, + 'message-card': { + DEFAULT: 'hsl(var(--message-card))', + foreground: 'hsl(var(--message-card-foreground))', + }, }, borderRadius: { lg: 'var(--radius)',