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

Add active/paused toggle #535

Merged
merged 12 commits into from
Oct 29, 2024
Merged

Add active/paused toggle #535

merged 12 commits into from
Oct 29, 2024

Conversation

PaulJKim
Copy link
Contributor

Asana task: Pause/Active toggle

Description
This PR adds a toggle to the PA Messages table that allows a user to pause or unpause a PA Message. The toggle updates the pause tag in the DB.

Additionally, if a PA Message is updated in a way where it is going to be either in or out of the active period of said PA Message, we will set the paused state to unpaused.

When a PA Message is set to paused, it should not trigger readouts. Note that this PR doesn't update the query that returns active PA Messages, which is also used by RTS when querying for active messages. I have the opinion that it would be simpler to do the filtering on the RTS client-side since if we filter out paused messages on the server, they won't be returned to Screenplay and therefore we won't show them in the PA Messages table anymore. We'd have to split it out into a different endpoint or add a parameter for including paused messages or something. I'm open to other thoughts though.

@PaulJKim PaulJKim requested a review from a team as a code owner October 21, 2024 23:04
}: PaMessageRowProps) => {
const navigate = useNavigate();
const start = new Date(paMessage.start_datetime);
const end =
paMessage.end_datetime === null ? null : new Date(paMessage.end_datetime);

const [paused, setPaused] = useState(!!paMessage.paused);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Rather than adding more state here, we could pass a callback that allows updating the underlying PA message state directly, and then just render that.

}: PaMessageRowProps) => {
const navigate = useNavigate();
const start = new Date(paMessage.start_datetime);
const end =
paMessage.end_datetime === null ? null : new Date(paMessage.end_datetime);

const [paused, setPaused] = useState(!!paMessage.paused);

const stopPropagation = (event: React.MouseEvent<HTMLDivElement>) => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

This variable could have a better name, e.g. togglePaused


const stopPropagation = (event: React.MouseEvent<HTMLDivElement>) => {
event.stopPropagation();
updateExistingPaMessage(paMessage.id, {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Without any error handling on this fetch, this could silently fail and leave the system in an unexpected state. We should at least be catching exceptions and checking that the result is ok, even if all we do is throw an error toast to let them know that something went wrong.

@digitalcora
Copy link
Contributor

Note that this PR doesn't update the query that returns active PA Messages, which is also used by RTS when querying for active messages.

I'd lean slightly in the other direction, given the concept of a message being "paused" is specific to Screenplay. I don't think RTS should have to also understand that — it should just get a list of messages it should play. If it also has to filter out paused messages, the "footprint" of this feature becomes larger and it could be harder to maintain.

We'd have to split it out into a different endpoint

I didn't realize the endpoint used by the Screenplay frontend and the endpoint used by RTS were actually the same. I think this is the solution I'd favor. We probably want to be able to do whatever we like with our "internal API" to the frontend, without affecting our external "contract" with RTS.

@PaulJKim
Copy link
Contributor Author

@digitalcora Actually I was sort of mistaken - they do use different endpoints, but they share the same query. If we just add a new query or some filtering that accounts for the paused state, that should be easy enough.

@@ -141,7 +159,7 @@ const PaMessagesPage: ComponentType = () => {
className={cx("button", { active: stateFilter === "active" })}
onClick={() => setStateFilter("active")}
>
Active
Now
Copy link
Contributor

Choose a reason for hiding this comment

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

Something I'm thinking about while working on the End Now task. We should also consider changing all references of active instead of just the label. This is a little more involved than normal because these values are passed to the server to help form the queries needed to filter the data. I still think it would be valuable to eliminate the inconsistent naming.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I thought about that too, but now just seems a little awkward to me as an adjective. eg get_now() is a little weird compared to get_active. At the same time, it would be helpful to have two distinct words for describing messages that are within their active period and unpaused versus just generally within their active period. I sort of wish we used current rather than now or something.

Copy link
Contributor

Choose a reason for hiding this comment

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

That's a good point. Maybe this is something to bring up to Mary? I didn't think about how it's kind of the outlier here not being an adjective (or at least an awkward one like you said). I agree with her that we can't use Active anymore, but your suggestion of Current sounds better to me.

Copy link
Contributor

Choose a reason for hiding this comment

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

No reason we couldn't use current as our internal name and "Now" as the user-facing name.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@digitalcora that works for me. I just pushed a change to use current internally.

paused: !paMessage.paused,
} as UpdatePaMessageBody);

if (result.status === 200) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is better, but it still doesn't catch network errors that cause the request to fail entirely. The way I usually like to handle this is to modify the API function (updateExistingPaMessage in this case) to include something like:

// using ok allows any successful response, e.g. 201
if (!response.ok) {
  const error = new Error("request failed")
  // attach any available error information from the server
  throw error
}

Now the API function's promise will resolve on success and reject on any failure, so you can use it like:

try {
  const payload = await updateExistingPaMessage(...)
  // do happy path stuff
}
catch (error) {
  // display error message
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thinking of the other usage of this API function on the EditPaMessage page if we modified the function this way, how would we attach the list of errors to the returned Error object when the server returns that? It doesn't seem like Error has a property that lets us easily pass along the response body.

Copy link
Collaborator

Choose a reason for hiding this comment

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

There are two approaches, and it kind of depends on how sophisticated the error reporting needs to be. If we just want to show a little diagnostic message, it might be enough to just read/parse the body and stick it in as the message string on the Error, and then just display that. If we need something fancier, then we could attach arbitrary data to the Error object by just assigning a custom property. The latter probably isn't needed, but I just wanted to mention it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

For the edit page, the current logic takes the object keys from the body and passes that array to the toast which renders a list of the errors the user needs to address. I tried an approach where we just throw the array itself as the error which seems okay to me. Let me know what your thoughts are though

Copy link
Collaborator

Choose a reason for hiding this comment

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

This seems good for now. If we like this pattern and want to apply it more generally in the future, we can formalize things by e.g. adding custom error subtypes with known data shapes, but that's probably premature at this point.

where:
^current_service_day_of_week in m.days_of_week and
m.start_datetime <= ^now and
((is_nil(m.end_datetime) and m.alert_id in ^alert_ids) or m.end_datetime >= ^now) and
Copy link
Collaborator

Choose a reason for hiding this comment

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

A big chunk of this clause looks the same as the one in current. Can we use composition somehow to reuse that?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ah yes, good point!

Copy link
Collaborator

@panentheos panentheos left a comment

Choose a reason for hiding this comment

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

Looks good, tested locally

@PaulJKim PaulJKim merged commit a26b200 into main Oct 29, 2024
2 checks passed
@PaulJKim PaulJKim deleted the pk/pause-active-toggle branch October 29, 2024 20:16
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