chore: import upstream snapshot with attribution
Publish CLI Package / publish-npm (push) Waiting to run
Publish Python SDK / publish-pypi (push) Waiting to run
Publish TypeScript SDK / publish-npm (push) Waiting to run
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 13:20:55 +08:00
commit d25d482dc2
13754 changed files with 4996608 additions and 0 deletions
+13
View File
@@ -0,0 +1,13 @@
import { Skeleton } from '@sim/emcn'
export default function UnsubscribeLoading() {
return (
<div className='flex flex-col items-center justify-center'>
<Skeleton className='size-[48px] rounded-[12px]' />
<Skeleton className='mt-[16px] h-[24px] w-[180px] rounded-[4px]' />
<Skeleton className='mt-[8px] h-[14px] w-[300px] rounded-[4px]' />
<Skeleton className='mt-[4px] h-[14px] w-[260px] rounded-[4px]' />
<Skeleton className='mt-[24px] h-[44px] w-[200px] rounded-[10px]' />
</div>
)
}
+11
View File
@@ -0,0 +1,11 @@
import type { Metadata } from 'next'
import Unsubscribe from '@/app/unsubscribe/unsubscribe'
export const metadata: Metadata = {
title: 'Unsubscribe',
robots: { index: false },
}
export const dynamic = 'force-dynamic'
export default Unsubscribe
+234
View File
@@ -0,0 +1,234 @@
'use client'
import { Suspense } from 'react'
import { Chip, cn, Loader } from '@sim/emcn'
import { getErrorMessage } from '@sim/utils/errors'
import { useSearchParams } from 'next/navigation'
import type { UnsubscribeType } from '@/lib/api/contracts/user'
import { AuthSubmitButton } from '@/app/(auth)/components'
import { AUTH_BUTTON_CLASS } from '@/app/(auth)/components/constants'
import { InviteLayout } from '@/app/invite/components'
import { useUnsubscribe, useUnsubscribeMutation } from '@/hooks/queries/unsubscribe'
function UnsubscribeContent() {
const searchParams = useSearchParams()
const email = searchParams.get('email')
const token = searchParams.get('token')
const hasParams = Boolean(email) && Boolean(token)
const query = useUnsubscribe(email ?? undefined, token ?? undefined)
const unsubscribe = useUnsubscribeMutation()
const data = query.data ?? null
const loading = hasParams && query.isLoading
const processing = unsubscribe.isPending
const unsubscribed = unsubscribe.isSuccess
const error = !hasParams
? 'Missing email or token in URL'
: query.isError
? getErrorMessage(query.error, 'Failed to validate unsubscribe link')
: unsubscribe.isError
? getErrorMessage(unsubscribe.error, 'Failed to process unsubscribe request')
: null
const handleUnsubscribe = (type: UnsubscribeType) => {
if (!email || !token) return
unsubscribe.mutate({ email, token, type })
}
if (loading) {
return (
<InviteLayout>
<div className='space-y-1 text-center'>
<h1 className={'font-medium text-[32px] text-[var(--text-primary)] tracking-tight'}>
Loading
</h1>
<p className={'font-[380] text-[var(--text-muted)] text-md'}>
Validating your unsubscribe link
</p>
</div>
<div className={'mt-8 flex w-full items-center justify-center py-8'}>
<Loader className='size-8 text-[var(--text-muted)]' animate />
</div>
</InviteLayout>
)
}
if (error) {
return (
<InviteLayout>
<div className='space-y-1 text-center'>
<h1 className={'font-medium text-[32px] text-[var(--text-primary)] tracking-tight'}>
Invalid Unsubscribe Link
</h1>
<p className={'font-[380] text-[var(--text-muted)] text-md'}>{error}</p>
</div>
<div className={'mt-8 w-full max-w-[410px] space-y-3'}>
<AuthSubmitButton type='button' onClick={() => window.history.back()} loadingLabel=''>
Go Back
</AuthSubmitButton>
</div>
</InviteLayout>
)
}
if (data?.isTransactional) {
return (
<InviteLayout>
<div className='space-y-1 text-center'>
<h1 className={'font-medium text-[32px] text-[var(--text-primary)] tracking-tight'}>
Important Account Emails
</h1>
<p className={'font-[380] text-[var(--text-muted)] text-md'}>
Transactional emails like password resets, account confirmations, and security alerts
cannot be unsubscribed from as they contain essential information for your account.
</p>
</div>
<div className={'mt-8 w-full max-w-[410px] space-y-3'}>
<AuthSubmitButton type='button' onClick={() => window.close()} loadingLabel=''>
Close
</AuthSubmitButton>
</div>
</InviteLayout>
)
}
if (unsubscribed) {
return (
<InviteLayout>
<div className='space-y-1 text-center'>
<h1 className={'font-medium text-[32px] text-[var(--text-primary)] tracking-tight'}>
Successfully Unsubscribed
</h1>
<p className={'font-[380] text-[var(--text-muted)] text-md'}>
You have been unsubscribed from our emails. You will stop receiving emails within 48
hours.
</p>
</div>
<div className={'mt-8 w-full max-w-[410px] space-y-3'}>
<AuthSubmitButton type='button' onClick={() => window.close()} loadingLabel=''>
Close
</AuthSubmitButton>
</div>
</InviteLayout>
)
}
const isAlreadyUnsubscribedFromAll = data?.currentPreferences.unsubscribeAll
return (
<InviteLayout>
<div className='space-y-1 text-center'>
<h1 className={'font-medium text-[32px] text-[var(--text-primary)] tracking-tight'}>
Email Preferences
</h1>
<p className={'font-[380] text-[var(--text-muted)] text-md'}>
Choose which emails you'd like to stop receiving.
</p>
<p className={'mt-2 font-[380] text-[var(--text-muted)] text-sm'}>{data?.email}</p>
</div>
<div className={'mt-8 w-full max-w-[410px] space-y-3'}>
<AuthSubmitButton
type='button'
onClick={() => handleUnsubscribe('all')}
disabled={isAlreadyUnsubscribedFromAll}
loading={processing}
loadingLabel='Unsubscribing'
>
{isAlreadyUnsubscribedFromAll
? 'Unsubscribed from All Emails'
: 'Unsubscribe from All Marketing Emails'}
</AuthSubmitButton>
<div className='py-2 text-center'>
<span className={'font-[380] text-[var(--text-muted)] text-sm'}>
or choose specific types
</span>
</div>
<Chip
fullWidth
flush
onClick={() => handleUnsubscribe('marketing')}
disabled={
processing ||
isAlreadyUnsubscribedFromAll ||
data?.currentPreferences.unsubscribeMarketing
}
className={cn(AUTH_BUTTON_CLASS, 'border border-[var(--border-1)]')}
>
{data?.currentPreferences.unsubscribeMarketing
? 'Unsubscribed from Marketing'
: 'Unsubscribe from Marketing Emails'}
</Chip>
<Chip
fullWidth
flush
onClick={() => handleUnsubscribe('updates')}
disabled={
processing ||
isAlreadyUnsubscribedFromAll ||
data?.currentPreferences.unsubscribeUpdates
}
className={cn(AUTH_BUTTON_CLASS, 'border border-[var(--border-1)]')}
>
{data?.currentPreferences.unsubscribeUpdates
? 'Unsubscribed from Updates'
: 'Unsubscribe from Product Updates'}
</Chip>
<Chip
fullWidth
flush
onClick={() => handleUnsubscribe('notifications')}
disabled={
processing ||
isAlreadyUnsubscribedFromAll ||
data?.currentPreferences.unsubscribeNotifications
}
className={cn(AUTH_BUTTON_CLASS, 'border border-[var(--border-1)]')}
>
{data?.currentPreferences.unsubscribeNotifications
? 'Unsubscribed from Notifications'
: 'Unsubscribe from Notifications'}
</Chip>
</div>
<div className={'mt-6 max-w-[410px] text-center'}>
<p className='font-[380] text-[var(--text-muted)] text-small'>
You'll continue receiving important account emails like password resets and security
alerts.
</p>
</div>
</InviteLayout>
)
}
export default function Unsubscribe() {
return (
<Suspense
fallback={
<InviteLayout>
<div className='space-y-1 text-center'>
<h1 className={'font-medium text-[32px] text-[var(--text-primary)] tracking-tight'}>
Loading
</h1>
<p className={'font-[380] text-[var(--text-muted)] text-md'}>
Validating your unsubscribe link
</p>
</div>
<div className={'mt-8 flex w-full items-center justify-center py-8'}>
<Loader className='size-8 text-[var(--text-muted)]' animate />
</div>
</InviteLayout>
}
>
<UnsubscribeContent />
</Suspense>
)
}