Skip to content
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

[RW-13480][risk=no] Workspace Banners for Initial Credits #8951

Open
wants to merge 32 commits into
base: main
Choose a base branch
from

Conversation

evrii
Copy link
Collaborator

@evrii evrii commented Nov 18, 2024

There are several different banner variants here. I tried to err on the side of readability even if that meant that the code was a little longer. With that said, I am certainly open to feedback if you have thoughts on how the code can be better organized.

In order to show each of these banners, you will need to manipulate five different states:

  • isCreator - whether or not you created the workspace that you are viewing
  • isExpired - whether the user's initial credit expiration date is in the past
  • isExpiringSoon - whether the user's initial credit expiration date is within X(initialCreditsExpirationWarningDays in config) days of expiring. This state is always checked after isExpired so it is checking: now>= expirationDate - initialCreditsExpirationWarningDays && now<expirationDate
  • isExhausted - whether or not a user has used all their credits
  • isEligible - whether or not a user is eligible for an extension of their initial credit expiration date: now >= expirationDate - initialCreditsExpirationWarningDays

Creator Expiring Soon Eligible for Extension
!isExpired && isExpiringSoon && !isExhausted && isEligible && isCreator
image

Non-creator Expiring Soon Eligible for Extension
!isExpired && isExpiringSoon && !isExhausted && isEligible && !isCreator
image

Creator Expired Eligible for Extension
isExpired && !isExhausted && isEligible && isCreator
image

[Non-creator Expired Eligible for Extension]
isExpired && !isExhausted && isEligible && !isCreator
image

Creator Expired Not Eligible for Extension
(isExpired || isExhausted) && !isEligible && isCreator
image

Non-creator Expired Not Eligible for Extension
(isExpired || isExhausted) && !isEligible && !isCreator
image


PR checklist

  • I have included an issue ID or "no ticket" in the PR title as outlined in CONTRIBUTING.md.
  • I have included a risk tag of the form [risk=no|low|moderate|severe] in the PR title as outlined in CONTRIBUTING.md.
  • I have manually tested this change and my testing process is described above.
  • This change includes appropriate automated tests, and I have documented any behavior that cannot be tested with code.
  • I have added explanatory comments where the logic is not obvious.
  • One or more of the following is true:
    • This change is intended to complete a JIRA story, so I have checked that all AC are met for that story.
    • This change fixes a bug, so I have ensured the steps to reproduce are in the Jira ticket or provided above.
    • This change impacts deployment safety (e.g. removing/altering APIs which are in use), so I have documented the impacts in the description.
    • This change includes a new feature flag, so I have created and linked new JIRA tickets to (a) turn on the feature flag and (b) remove it later.
    • This change modifies the UI, so I have taken screenshots or recordings of the new behavior and notified the PO and UX designer in Slack.
    • This change modifies API behavior, so I have run the relevant E2E tests locally because API changes are not covered by our PR checks.
    • None of the above apply to this change.

plusDays(workspace.initialCredits.expirationEpochMillis, 5) < Date.now();
let message;
let title;
if (isExpiringSoon && isEligibleForExtension) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels complicated, but the only way that I can think of simplifying it is to break up individual phrases into variables and then have several ternary statements. I feel like that might be more efficient, but I'm not sure it would be more readable.

I would love thoughts here.

Copy link
Collaborator

@jmthibault79 jmthibault79 Nov 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

your comment refers to an outdated version. is there a similar comment for the updated version?

@evrii evrii changed the base branch from main to nsaxena/FixPuppeterTest November 19, 2024 17:02
@evrii evrii changed the base branch from nsaxena/FixPuppeterTest to main November 19, 2024 17:03
@evrii evrii closed this Nov 19, 2024
@evrii evrii reopened this Nov 19, 2024
zIndex={500}
/>
<>
{((isExpiringSoon && isEligibleForExtension) || isExpired) && (
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no reason to show this banner if one of these conditions is not true.

This component is only used by the breadcrumbs component. So this condition could be shifted up, but these conditions would need to be recalculated. In that case, if appropriate, breadcrumbs could calculate these values and then pass them down.

The only other usage of workspace in this component is the construction of the link associated with the "Edit Workspace" button.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In that case, why is isExhausted a parameter to the text functions used to construct message? If only isExhausted is true, then we don't need message.

@evrii evrii changed the title Eric/rw 13480 [RW-13480][risk=no] Workspace Banners for Initial Credits Nov 22, 2024
@evrii evrii marked this pull request as ready for review November 22, 2024 21:29
@Mapping(
target = "initialCredits.extensionEpochMillis",
source = "dbWorkspace.creator",
qualifiedByName = "getInitialCreditsExpiration")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this sets extension to the same value as expiration. Is this intentional?

plusDays(workspace.initialCredits.expirationEpochMillis, 5) < Date.now();
let message;
let title;
if (isExpiringSoon && isEligibleForExtension) {
Copy link
Collaborator

@jmthibault79 jmthibault79 Nov 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

your comment refers to an outdated version. is there a similar comment for the updated version?

@@ -19,23 +23,167 @@ interface Props extends NavigationProps {
onClose: Function;
}

const InitialCreditsArticleLink = () => (
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these 2 links have different text but the same URL. that does not seem right.

const isExpiringSoon =
workspace &&
!isExpired &&
plusDays(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could use your new minusDays here

) => {
const whose = whoseCredits(isCreator);
let whatIsHappening: string;
if (isExhausted || (isExpired && !isEligibleForExtension)) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you were looking for a way to simplify the logic? I like these function names and I think the outputs make sense, so I'd keep those.

Maybe one way to simplify the logic would involve an enum of possible states, named something like ExtensionCondition. Before calling these text functions, we would call another function determineCondition() to generate the enum. then these text functions become switch or cond statements, something like:

case EXHAUSTED | EXPIRED_CANNOT_EXTEND: ...
case EXPIRED_CAN_EXTEND: ...
case EXPIRING_SOON_OWNER_CAN_EXTEND | EXPIRING_SOON_NONOWNER_CAN_EXTEND: ...

I don't know that this would actually be better.

whatIsHappening = 'have run out.';
} else if (isExpired && isEligibleForExtension) {
whatIsHappening = 'have expired.';
} else if (isExpiringSoon && isEligibleForExtension) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what happens in the case of isExpiringSoon && !isEligibleForExtension? If that never happens here, do you mind adding a comment?

} else {
return (
<>
{isEligibleForExtension && (isExpired || isExpiringSoon) && (
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

because this section does not mention adding a billing account, the Edit Workspace button lacks the context that explains why it's there. From a program perspective, adding a billing account is the preferred outcome, so I think we should suggest that whenever possible.

render(
<MemoryRouter>
<InvalidBillingBanner
{...{ signatureState }}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this doesn't look right. None of these props are used by InvalidBillingBanner.

beforeEach(() => {
registerApiClient(ProfileApi, new ProfileApiStub());

// Do I need this?
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so

// Set expiration date to be in the past if expired, in the future if not.
// If expiring soon, set it to be within the warning threshold, and just outside otherwise.
// Expired and expiringSoon are mutually exclusive.
const daysUntilExpiration = expired
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this

};

it('should show expiring soon banner to user who created the workspace', async () => {
setupWorkspace(false, false, true, true);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer to see these made explicit, like

    const exhausted = false;
    const expired = false;
    const expiringSoon = true;
    const ownedByMe = true;
    setupWorkspace(exhausted, expired, expiringSoon, ownedByMe);

);
});

it('should show expired banner to user who did not create the workspace and the owner is not eligible for extension', async () => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like to see "exhausted" tests as well

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants