Openstatus www.openstatus.dev

๐Ÿฆ trying new pricing name (#1230)

* ๐Ÿฆ trying new pricing name

* ci: apply automated fixes

* ๐Ÿ”ฅ fix ubild

* ๐Ÿ˜‚ build

* ๐Ÿงช fix tests

* ๐Ÿงน

* fix: ui stuff

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Maximilian Kaske <maximilian@kaske.org>

authored by

Thibault Le Ouay
autofix-ci[bot]
Maximilian Kaske
and committed by
GitHub
68dd22bb 88ef0684

+57 -129
+1 -1
apps/web/src/app/(content)/features/status-page/page.tsx
··· 72 72 subTitle="Let your users subscribe to your status page, to automatically receive updates about the status of your services." 73 73 component={ 74 74 <div className="m-auto"> 75 - <SubscribeButton plan="pro" slug={""} isDemo /> 75 + <SubscribeButton plan="team" slug={""} isDemo /> 76 76 </div> 77 77 } 78 78 col={1}
-1
apps/web/src/app/app/[workspaceSlug]/(dashboard)/settings/billing/_components/plan.tsx
··· 47 47 // REMINDER: redirecting to customer portal as a fallback because the free plan has no price 48 48 free: getUserCustomerPortal, 49 49 starter: () => getCheckoutSession("starter"), 50 - pro: () => getCheckoutSession("pro"), 51 50 team: () => getCheckoutSession("team"), 52 51 }} 53 52 />
+1 -1
apps/web/src/components/billing/pro-banner.tsx
··· 46 46 <div className="grid gap-2 rounded-md border border-border p-3"> 47 47 <div className="flex items-center justify-between"> 48 48 <p className="inline-flex items-center font-medium text-sm"> 49 - OpenStatus Pro <Rocket className="ml-2 h-4 w-4" /> 49 + OpenStatus Subscription <Rocket className="ml-2 h-4 w-4" /> 50 50 </p> 51 51 <Button 52 52 variant="ghost"
+1 -1
apps/web/src/components/billing/pro-feature-alert.tsx
··· 12 12 workspacePlan?: WorkspacePlan; 13 13 } 14 14 15 - export function ProFeatureAlert({ feature, workspacePlan = "pro" }: Props) { 15 + export function ProFeatureAlert({ feature, workspacePlan = "team" }: Props) { 16 16 const params = useParams<{ workspaceSlug: string }>(); 17 17 return ( 18 18 <Alert>
+38 -20
apps/web/src/components/marketing/pricing/enterprice-plan.tsx
··· 1 1 import { Button } from "@openstatus/ui/src/components/button"; 2 + import { CheckIcon } from "lucide-react"; 3 + 4 + const features = [ 5 + "Custom Integration", 6 + "Custom Limits", 7 + "Custom Support", 8 + "Custom regions", 9 + "SSO/SAML", 10 + "BYOC (Bring Your Own Cloud)", 11 + ]; 2 12 3 13 export function EnterpricePlan() { 4 14 return ( 5 - <div className="flex w-full flex-col gap-3"> 6 - <div className="flex-1"> 7 - <div className="flex items-end justify-between gap-4"> 8 - <div> 9 - <p className="mb-2 font-cal text-2xl">Custom</p> 10 - <p className="text-muted-foreground"> 11 - Want more regions? Want to host it on your own server? Want 12 - something else? We can help you with that. 13 - </p> 15 + <div> 16 + <p className="mb-2 font-cal text-2xl">Enterprise</p> 17 + <p className="text-muted-foreground">If you are looking for:</p> 18 + <div className="my-4 grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4"> 19 + {features.map((feature) => ( 20 + <div key={feature} className="flex items-center"> 21 + <CheckIcon className="mr-1.5 h-4 w-4 shrink-0" /> 22 + <span className="text-foreground text-sm">{feature}</span> 14 23 </div> 15 - </div> 24 + ))} 16 25 </div> 17 - <div> 18 - <Button className="rounded-full" asChild> 19 - <a 20 - href="https://cal.com/team/openstatus/30min" 21 - target="_blank" 22 - rel="noreferrer" 23 - > 24 - Talk to us 25 - </a> 26 - </Button> 26 + <div className="grid grid-cols-1 sm:grid-cols-4 gap-6"> 27 + <div className="sm:col-span-3"> 28 + <p className="text-muted-foreground"> 29 + We can help you with that. Speak with us today to build your own 30 + custom solution that fits your needs. 31 + </p> 32 + </div> 33 + <div className="sm:col-span-1 w-full"> 34 + <Button className="rounded-full w-full" asChild> 35 + <a 36 + href="https://cal.com/team/openstatus/30min" 37 + target="_blank" 38 + rel="noreferrer" 39 + className="text-nowrap" 40 + > 41 + Talk to us 42 + </a> 43 + </Button> 44 + </div> 27 45 </div> 28 46 </div> 29 47 );
+1 -1
apps/web/src/components/marketing/pricing/pricing-plan-radio.tsx
··· 19 19 return ( 20 20 <RadioGroup 21 21 defaultValue="team" 22 - className="grid grid-cols-4 gap-4" 22 + className="grid grid-cols-3 gap-4" 23 23 onValueChange={onChange} 24 24 > 25 25 {workspacePlans.map((key) => (
+5 -5
apps/web/src/components/marketing/pricing/pricing-table.tsx
··· 45 45 .filter(([key, _]) => plans.includes(key as keyof typeof allPlans)) 46 46 .map(([key, value]) => ({ key: key as keyof typeof allPlans, ...value })); 47 47 return ( 48 - <Table className="relative"> 48 + <Table className="relative table-fixed"> 49 49 <TableCaption> 50 50 A list to compare the different features by plan. 51 51 </TableCaption> ··· 61 61 key={key} 62 62 className={cn( 63 63 "h-auto px-3 py-3 align-bottom text-foreground", 64 - key === "team" ? "bg-muted/30" : "bg-background", 64 + key === "starter" ? "bg-muted/30" : "bg-background", 65 65 )} 66 66 > 67 67 <p className="sticky top-0 mb-2 font-cal text-2xl"> ··· 79 79 <Button 80 80 className="w-full" 81 81 size="sm" 82 - variant={key === "team" ? "default" : "outline"} 82 + variant={key === "starter" ? "default" : "outline"} 83 83 onClick={() => { 84 84 if (events?.[key]) { 85 85 return events[key]?.(); ··· 92 92 > 93 93 {isLoading ? ( 94 94 <LoadingAnimation 95 - variant={key === "team" ? "default" : "inverse"} 95 + variant={key === "starter" ? "default" : "inverse"} 96 96 /> 97 97 ) : isCurrentPlan ? ( 98 98 "Current plan" ··· 178 178 key={key + value + _i} 179 179 className={cn( 180 180 "p-3 font-mono", 181 - plan.key === "team" && "bg-muted/30", 181 + plan.key === "starter" && "bg-muted/30", 182 182 )} 183 183 > 184 184 {renderContent()}
-6
apps/web/src/config/pricing-table.tsx
··· 51 51 }, 52 52 { value: "max-regions", label: "Number of Regions" }, 53 53 { value: "data-retention", label: "Data retention" }, 54 - { value: "screenshots", label: "Screenshots upon failure" }, 55 54 { value: "otel", label: "OTel Exporter" }, 56 55 ], 57 56 }, ··· 63 62 label: "Number of on-demand checks", 64 63 monthly: true, 65 64 }, 66 - { value: "private-locations", label: "Private Locations" }, 67 65 ], 68 66 }, 69 67 "status-pages": { ··· 98 96 description: renderChangelogDescription( 99 97 "password-protected-status-page", 100 98 ), 101 - }, 102 - { 103 - value: "white-label", 104 - label: "White Label", 105 99 }, 106 100 ], 107 101 },
-11
packages/api/src/router/stripe/utils.ts
··· 15 15 }; 16 16 export const PLANS = [ 17 17 { 18 - plan: "pro", 19 - price: { 20 - monthly: { 21 - priceIds: { 22 - test: "price_1NdurjBXJcTfzsyJdAzIxXnT", 23 - production: "price_1PdJ3lBXJcTfzsyJHgqcfVG9", 24 - }, 25 - }, 26 - }, 27 - }, 28 - { 29 18 plan: "team", 30 19 price: { 31 20 monthly: {
+3 -3
packages/api/src/router/workspace.test.ts
··· 25 25 id: 1, 26 26 slug: "love-openstatus", 27 27 name: "test", 28 - plan: "pro", 28 + plan: "team", 29 29 paidUntil: null, 30 30 stripeId: "stripeId1", 31 31 subscriptionId: "subscriptionId", ··· 54 54 id: 1, 55 55 slug: "love-openstatus", 56 56 name: "test", 57 - plan: "pro", 57 + plan: "team", 58 58 paidUntil: null, 59 59 stripeId: "stripeId1", 60 60 subscriptionId: "subscriptionId", ··· 96 96 id: 1, 97 97 name: "test", 98 98 paidUntil: null, 99 - plan: "pro", 99 + plan: "team", 100 100 slug: "love-openstatus", 101 101 stripeId: "stripeId1", 102 102 subscriptionId: "subscriptionId",
-3
packages/api/src/router/workspace.ts
··· 184 184 case "team": { 185 185 break; 186 186 } 187 - case "pro": { 188 - break; 189 - } 190 187 default: { 191 188 } 192 189 }
+5 -73
packages/db/src/schema/plan/config.ts
··· 5 5 export const allPlans: Record< 6 6 WorkspacePlan, 7 7 { 8 - title: "Hobby" | "Starter" | "Growth" | "Pro"; 8 + title: "Hobby" | "Starter" | "Pro"; 9 9 id: WorkspacePlan; 10 10 description: string; 11 11 price: number; ··· 15 15 free: { 16 16 title: "Hobby", 17 17 id: "free", 18 - description: "For personal projects", 18 + description: "Perfect for personal projects", 19 19 price: 0, 20 20 limits: { 21 21 monitors: 1, ··· 47 47 starter: { 48 48 title: "Starter", 49 49 id: "starter", 50 - description: "For small projects", 50 + description: "Perfect for uptime monitoring", 51 51 price: 30, 52 52 limits: { 53 53 monitors: 5, ··· 113 113 }, 114 114 }, 115 115 team: { 116 - title: "Growth", 116 + title: "Pro", 117 117 id: "team", 118 - description: "For small teams", 118 + description: "Perfect for global synthetic monitoring", 119 119 price: 100, 120 120 limits: { 121 121 monitors: 15, ··· 178 178 "yyz", 179 179 ], 180 180 "private-locations": false, 181 - }, 182 - }, 183 - pro: { 184 - title: "Pro", 185 - id: "pro", 186 - description: "For bigger teams", 187 - price: 300, 188 - limits: { 189 - monitors: 50, 190 - "synthetic-checks": 500, 191 - periodicity: ["30s", "1m", "5m", "10m", "30m", "1h"], 192 - "multi-region": true, 193 - "max-regions": 35, 194 - "data-retention": "24 months", 195 - "status-pages": 20, 196 - maintenance: true, 197 - "monitor-values-visibility": true, 198 - screenshots: true, 199 - otel: true, 200 - "status-subscribers": true, 201 - "custom-domain": true, 202 - "password-protection": true, 203 - "white-label": true, 204 - notifications: true, 205 - sms: true, 206 - pagerduty: true, 207 - opsgenie: true, 208 - "notification-channels": 50, 209 - members: "Unlimited", 210 - "audit-log": true, 211 - regions: [ 212 - "ams", 213 - "arn", 214 - "atl", 215 - "bog", 216 - "bom", 217 - "bos", 218 - "cdg", 219 - "den", 220 - "dfw", 221 - "ewr", 222 - "eze", 223 - "fra", 224 - "gdl", 225 - "gig", 226 - "gru", 227 - "hkg", 228 - "iad", 229 - "jnb", 230 - "lax", 231 - "lhr", 232 - "mad", 233 - "mia", 234 - "nrt", 235 - "ord", 236 - "otp", 237 - "phx", 238 - "qro", 239 - "scl", 240 - "sea", 241 - "sin", 242 - "sjc", 243 - "syd", 244 - "waw", 245 - "yul", 246 - "yyz", 247 - ], 248 - "private-locations": true, 249 181 }, 250 182 }, 251 183 };
+1 -2
packages/db/src/schema/workspaces/constants.ts
··· 1 - export const workspacePlans = ["free", "starter", "team", "pro"] as const; 1 + export const workspacePlans = ["free", "starter", "team"] as const; 2 2 export const workspaceRole = ["owner", "admin", "member"] as const; 3 3 4 4 export const workspacePlanHierarchy: Record< ··· 8 8 free: 0, 9 9 starter: 1, 10 10 team: 2, 11 - pro: 3, 12 11 };
+1 -1
packages/db/src/seed.mts
··· 34 34 stripeId: "stripeId1", 35 35 name: "test", 36 36 subscriptionId: "subscriptionId", 37 - plan: "pro", 37 + plan: "team", 38 38 endsAt: null, 39 39 paidUntil: null, 40 40 limits: