-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Fix remaining issues for simplified team plans #10182
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
3718579
4de3308
2c1f29a
36f5485
f27b0e0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,7 +10,8 @@ import ContextMenu from "./ContextMenu"; | |
|
||
export interface DropDownProps { | ||
prefix?: string; | ||
contextMenuWidth?: string; | ||
customClasses?: string; | ||
renderAsLink?: boolean; | ||
activeEntry?: string; | ||
entries: DropDownEntry[]; | ||
} | ||
|
@@ -35,14 +36,20 @@ function DropDown(props: DropDownProps) { | |
}, | ||
}; | ||
}); | ||
const font = | ||
"text-gray-400 dark:text-gray-500 text-sm leading-1 group hover:text-gray-600 dark:hover:text-gray-400 transition ease-in-out"; | ||
const defaultFont = "text-gray-400 dark:text-gray-500 hover:text-gray-600 dark:hover:text-gray-400 "; | ||
const asLinkFont = "text-blue-500 dark:text-blue-400 hover:text-blue-600 dark:hover:text-blue-500"; | ||
const asLinkArrowBorder = | ||
"border-blue-500 dark:border-blue-400 group-hover:border-blue-600 dark:group-hover:border-blue-500"; | ||
return ( | ||
<ContextMenu menuEntries={enhancedEntries} classes={`${props.contextMenuWidth} right-0`}> | ||
<span className={`py-2 cursor-pointer ${font}`}> | ||
<ContextMenu menuEntries={enhancedEntries} customClasses={`${props.customClasses} right-0`}> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. thought: Wondering if we need the custom classes altogether here. π There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe we do, because the context-menu sometimes need to be left-aligned or right-aligned, among other things. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's better to only use left-aligned items inside the dropdown menus. π‘ Do you have a handy instance of a right-alignment you're referring to? I've only seen the dropdown in the workspace start page when using a desktop editor that's using center alignment and this should also change, see #6910. If this is about the alignment of the component itself with the rest of the elements around it, we could probably move the alignment control outside this component, right? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, thanks for clarifying, @jankeromnes! |
||
<span | ||
className={`py-2 cursor-pointer text-sm leading-1 group ${ | ||
props.renderAsLink ? asLinkFont : defaultFont | ||
} transition ease-in-out`} | ||
> | ||
jankeromnes marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{props.prefix} | ||
{current} | ||
<Arrow up={false} /> | ||
<Arrow up={false} customBorderClasses={props.renderAsLink ? asLinkArrowBorder : undefined} /> | ||
</span> | ||
</ContextMenu> | ||
); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -143,13 +143,14 @@ export default function TeamBilling() { | |
title="Billing" | ||
subtitle="Manage team billing and plans." | ||
> | ||
<h3>{!teamPlan ? "No billing plan" : "Plan"}</h3> | ||
<h3>{!teamPlan ? "Upgrade Team Plan" : "Team Plan"}</h3> | ||
<h2 className="text-gray-500"> | ||
{!teamPlan ? ( | ||
<div className="flex space-x-1"> | ||
<span>Select a new billing plan for this team. Currency:</span> | ||
<span>Upgrade team plan to access unlimited workspace hours, and more. Currency:</span> | ||
<DropDown | ||
contextMenuWidth="w-32" | ||
customClasses="w-32" | ||
renderAsLink={true} | ||
activeEntry={currency} | ||
entries={[ | ||
{ | ||
|
@@ -172,12 +173,12 @@ export default function TeamBilling() { | |
<div className="mt-4 space-x-4 flex"> | ||
{isLoading && ( | ||
<> | ||
<SolidCard> | ||
<SolidCard className="w-72 h-64"> | ||
<div className="w-full h-full flex flex-col items-center justify-center"> | ||
<Spinner className="h-5 w-5 animate-spin" /> | ||
</div> | ||
</SolidCard> | ||
<SolidCard> | ||
<SolidCard className="w-72 h-64"> | ||
<div className="w-full h-full flex flex-col items-center justify-center"> | ||
<Spinner className="h-5 w-5 animate-spin" /> | ||
</div> | ||
|
@@ -188,14 +189,22 @@ export default function TeamBilling() { | |
<> | ||
{availableTeamPlans.map((tp) => ( | ||
<> | ||
<SolidCard | ||
className="cursor-pointer hover:bg-gray-200 dark:hover:bg-gray-700" | ||
onClick={() => checkout(tp)} | ||
> | ||
<SolidCard className="w-72 h-72"> | ||
<div className="px-2 py-5 flex-grow flex flex-col"> | ||
<div className="font-medium text-base">{tp.name}</div> | ||
<div className="font-semibold text-gray-500 text-sm">Unlimited hours</div> | ||
<div className="mt-8 font-semibold text-sm">Includes:</div> | ||
<div className="font-semibold text-gray-800 dark:text-gray-100 text-lg"> | ||
{tp.name} | ||
</div> | ||
<div className="font-semibold text-gray-400 dark:text-gray-600 text-sm"> | ||
Unlimited hours | ||
</div> | ||
<div className="mt-2"> | ||
<PillLabel type="warn" className="font-semibold normal-case text-sm"> | ||
jankeromnes marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{members.length} x {Currency.getSymbol(tp.currency)} | ||
{tp.pricePerMonth} = {Currency.getSymbol(tp.currency)} | ||
{members.length * tp.pricePerMonth} per month | ||
</PillLabel> | ||
</div> | ||
<div className="mt-4 font-semibold text-sm">Includes:</div> | ||
<div className="flex flex-col items-start text-sm"> | ||
{(featuresByPlanType[tp.type] || []).map((f) => ( | ||
<span className="inline-flex space-x-1"> | ||
|
@@ -204,12 +213,10 @@ export default function TeamBilling() { | |
</span> | ||
))} | ||
</div> | ||
<div className="flex-grow flex flex-col items-end justify-end"> | ||
<PillLabel type="warn" className="font-semibold normal-case text-sm"> | ||
{members.length} x {Currency.getSymbol(tp.currency)} | ||
{tp.pricePerMonth} = {Currency.getSymbol(tp.currency)} | ||
{members.length * tp.pricePerMonth} per month | ||
</PillLabel> | ||
<div className="flex-grow flex flex-col items-stretch justify-end"> | ||
<button className="m-0" onClick={() => checkout(tp)}> | ||
Upgrade to {tp.name} | ||
</button> | ||
</div> | ||
</div> | ||
</SolidCard> | ||
|
@@ -219,10 +226,14 @@ export default function TeamBilling() { | |
)} | ||
{!isLoading && teamPlan && ( | ||
<> | ||
<Card> | ||
<Card className="w-72 h-64"> | ||
<div className="px-2 py-5 flex-grow flex flex-col"> | ||
<div className="font-bold text-base">{teamPlan.name}</div> | ||
<div className="font-semibold text-gray-500 text-sm">Unlimited hours</div> | ||
<div className="font-semibold text-gray-100 dark:text-gray-800 text-lg"> | ||
{teamPlan.name} | ||
</div> | ||
<div className="font-semibold text-gray-400 dark:text-gray-600 text-sm"> | ||
Unlimited hours | ||
</div> | ||
<div className="mt-8 font-semibold text-sm">Includes:</div> | ||
<div className="flex flex-col items-start text-sm"> | ||
{(featuresByPlanType[teamPlan.type] || []).map((f) => ( | ||
|
@@ -232,25 +243,31 @@ export default function TeamBilling() { | |
</span> | ||
))} | ||
</div> | ||
<div className="flex-grow flex flex-col items-end justify-end"></div> | ||
<div className="flex-grow flex flex-col items-stretch justify-end"></div> | ||
</div> | ||
</Card> | ||
{!teamSubscription ? ( | ||
<SolidCard> | ||
<SolidCard className="w-72 h-64"> | ||
<div className="w-full h-full flex flex-col items-center justify-center"> | ||
<Spinner className="h-5 w-5 animate-spin" /> | ||
</div> | ||
</SolidCard> | ||
) : ( | ||
<SolidCard> | ||
<SolidCard className="w-72 h-64"> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. thought: Hoping in the future we can drop these by introducing component variants that include card size, etc. instead of hard-coding sizes like this. We'll get there! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There are ways to achieve this with a single component, e.g. by using:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, but let's keep this as is for now until the card component matures enough from design perspective to set a foundation of card sizes, size steps, etc. Long-term, to keep the component flexible I'd love to introduce a stepper property to increase the width or height without using Tailwind's classes. π§‘ But no need to do this in the scope of this PR. πΉ There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, it's good to keep in mind that Tailwind is so nice because it allows directly "hard-coding" style in a nice way (as opposed to hiding style under multiple levels of abstractions, making them really hard to adjust afterwards). π There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
But, even better would be if the Cards always look perfectly-sized, depending on their contents, without needing a stepper or Tailwind classes, right? π ("Default correct" is better than "lots of knobs and switches") There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I'm also in favor of duplication over abstraction as it comes with multiple benefits, but in the long run it makes sense to abstract some component aspects to help a) avoid running into code reviews nitpicking dimensions or other attributes, b) avoid requesting UX reviews when possible to avoid, c) feeling more confident about the product UX, and more. However, this issue will become more relevant as these components mature and are widely used. For now, as long as we have two component instances of the solid card component, any approach would suffice and does not worth keeping this PR from being merged. π€
Debatable! π In most cases, the card component would need to use a specified width or height, which could be challenging to maintain when having multiple cards side-by-side that from a design perspective have to use fixed and specific dimensions regardless if some contents are missing. For example, see editor preferences, etc. However, there's still need for having flexible card layouts without fixed width that fill the parent container on smaller viewports, etc. |
||
<div className="px-2 py-5 flex-grow flex flex-col"> | ||
<div className="font-medium text-base text-gray-400">Members</div> | ||
<div className="font-semibold text-base text-gray-600">{members.length}</div> | ||
<div className="mt-8 font-medium text-base text-gray-400">Next invoice on</div> | ||
<div className="font-semibold text-base text-gray-600"> | ||
<div className="font-medium text-base text-gray-400 dark:text-gray-600"> | ||
Members | ||
</div> | ||
<div className="font-semibold text-base text-gray-600 dark:text-gray-400"> | ||
{members.length} | ||
</div> | ||
<div className="mt-4 font-medium text-base text-gray-400 dark:text-gray-600"> | ||
Next invoice on | ||
</div> | ||
<div className="font-semibold text-base text-gray-600 dark:text-gray-400"> | ||
{guessNextInvoiceDate(teamSubscription.startDate).toDateString()} | ||
</div> | ||
<div className="flex-grow flex flex-col items-end justify-end"> | ||
<div className="flex-grow flex flex-col items-stretch justify-end"> | ||
<button | ||
onClick={() => { | ||
if (team) { | ||
|
Uh oh!
There was an error while loading. Please reload this page.