Skip to content

feat: enhance mapping dialog UI with team filtering and improved styling#139

Open
pdt-rmunoz wants to merge 2 commits intoservice_mappigns_featurefrom
pdt-rmunoz/mapping-dialog-enhancement-v2
Open

feat: enhance mapping dialog UI with team filtering and improved styling#139
pdt-rmunoz wants to merge 2 commits intoservice_mappigns_featurefrom
pdt-rmunoz/mapping-dialog-enhancement-v2

Conversation

@pdt-rmunoz
Copy link

Description

This commit adds comprehensive improvements to the mapping dialog:

Backend changes:

  • Add getAllTeams endpoint to fetch all PagerDuty teams
  • Add getFilteredServices endpoint with team_id and query filters
  • Add PagerDutyTeam type to common types

Frontend changes:

  • Add team filtering dropdown to filter services by PagerDuty team
  • Add service search with debounced input
  • Replace read-only text displays with TextField components for visual consistency
  • Add border container styling to RadioGroup matching TextField appearance
  • Add enhanced radio button styling with interactive states
image image image

Issue number:
https://pagerduty.atlassian.net/browse/DEVECO-527

Affected plugin

  • backstage-plugin
  • backstage-plugin-backend
  • backstage-plugin-scaffolder-actions
  • backstage-plugin-entity-processor

Type of change

  • New feature (non-breaking change which adds functionality)
  • Fix (non-breaking change which fixes an issue)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)

Checklist

  • I have performed a self-review of this change
  • Changes have been tested
  • Changes have been tested in dark theme
  • Changes are documented
  • Changes generate no new warnings
  • PR title follows conventional commit semantics

If this is a breaking change 👇

  • I have documented the migration process
  • I have implemented necessary warnings (if it can live side by side)

Acknowledgement

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

Disclaimer: We value your time and bandwidth. As such, any pull requests created on non-triaged issues might not be successful.

This commit adds comprehensive improvements to the mapping dialog:

Backend changes:
- Add getAllTeams endpoint to fetch all PagerDuty teams
- Add getFilteredServices endpoint with team_id and query filters
- Add PagerDutyTeam type to common types

Frontend changes:
- Add team filtering dropdown to filter services by PagerDuty team
- Add service search with debounced input (500ms)
- Replace read-only text displays with TextField components for visual consistency
- Add border container styling to RadioGroup matching TextField appearance
- Add enhanced radio button styling with interactive states

Radio button improvements:
- Individual padding on each option (12px all sides)
- Bottom border dividers between options
- Hover effect with gray background
- Selected state with blue background, bold text, and 3px left accent bar

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@pdt-rmunoz pdt-rmunoz requested a review from a team as a code owner February 25, 2026 00:46
Copy link
Contributor

@sandornagy517 sandornagy517 left a comment

Choose a reason for hiding this comment

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

great job! just left some small comments

Object.keys(EndpointConfig).map(async account => {
try {
// reset offset value
offset = 0;
Copy link
Contributor

Choose a reason for hiding this comment

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

these comments are a bit redundant, could you please remove them?

Copy link
Author

Choose a reason for hiding this comment

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

solve it

let params = `time_zone=UTC&limit=${limit}`;

// Add team_ids filter if provided
if (teamIds && teamIds.length > 0) {
Copy link
Contributor

Choose a reason for hiding this comment

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

same for the comments here

Copy link
Author

Choose a reason for hiding this comment

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

removed

const [searchQuery, setSearchQuery] = useState<string>('');
const [debouncedSearchQuery, setDebouncedSearchQuery] = useState<string>('');

// Debounce search query (wait 500ms after user stops typing)
Copy link
Contributor

Choose a reason for hiding this comment

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

same for the comments

Copy link
Author

Choose a reason for hiding this comment

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

removed

<Flex>
<Text variant="body-medium" weight="regular">
Name:
<style>
Copy link
Contributor

Choose a reason for hiding this comment

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

we should use makeStyles and useStyles for these

Copy link
Author

Choose a reason for hiding this comment

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

Added import { makeStyles } from '@material-ui/core'
Created useStyles hook with nested selectors

<>
<Box
className="radio-list-container"
style={{
Copy link
Contributor

Choose a reason for hiding this comment

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

same for styles like above

Copy link
Author

Choose a reason for hiding this comment

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

same as above

- Remove redundant comments from pagerduty.ts functions (getAllTeams, getFilteredServices)
- Remove redundant comments from MappingsDialog.tsx
- Refactor inline <style> tag to use makeStyles from @material-ui/core
- Refactor inline Box styles to use makeStyles pattern
- Follow established codebase pattern for styling (StatusCell, AutoMappingsButton)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@jhfgloria
Copy link
Contributor

Is it me, or the first option is smaller than the others, @pdt-rmunoz?

image

Copy link
Contributor

@jhfgloria jhfgloria left a comment

Choose a reason for hiding this comment

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

Left some questions and comments. Overall really nice PR.

Comment on lines +1104 to +1125
// GET /filtered-services?team_id=:teamId&query=:query&limit=:limit
router.get('/filtered-services', async (request, response) => {
try {
const teamId = request.query.team_id as string | undefined;
const query = request.query.query as string | undefined;
const limit = request.query.limit
? parseInt(request.query.limit as string, 10)
: 100;

const teamIdsArray: string[] | undefined = teamId ? [teamId] : undefined;

const services = await getFilteredServices(teamIdsArray, query, limit);

response.json(services);
} catch (error) {
if (error instanceof HttpError) {
response.status(error.status).json({
errors: [`${error.message}`],
});
}
}
});
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think we should have an extra method here. I think this method is just a specialisation of the next method /services. You should merge them together, and have a different execution path for either a team_id search, or a integration_key search. Otherwise we will end up with a multitude of controller methods for every different query string someone decides to introduce.

const res = await getTeams(offset, limit, account);

res[1].forEach(team => {
team.account = account;
Copy link
Contributor

Choose a reason for hiding this comment

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

We need to be careful about this one. Is this setting it as default? I think I mentioned this to @sandornagy517 before, that we should avoid using the default value, which I believe is the case here.

Copy link
Contributor

Choose a reason for hiding this comment

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

image I've already added this account selector, maybe we should merge that PR first and then adjust this one?

Comment on lines +71 to +77
useEffect(() => {
const timer = setTimeout(() => {
setDebouncedSearchQuery(searchQuery);
}, 500);

return () => clearTimeout(timer);
}, [searchQuery]);
Copy link
Contributor

Choose a reason for hiding this comment

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

I think @sandornagy517 created a new useDebounce hook that abstracts this login.

Suggested change
useEffect(() => {
const timer = setTimeout(() => {
setDebouncedSearchQuery(searchQuery);
}, 500);
return () => clearTimeout(timer);
}, [searchQuery]);
const debouncedSearchQuery = useDebounced(searchQuery);

Comment on lines +148 to +153
createMapping({
serviceId: currentServiceId,
integrationKey: currentIntegrationKey,
entityRef: '',
account: account,
});
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm wondering if we shouldn't remove the mapping entirely... 🤔

value={selectedTeamIds[0] || ''}
onChange={value => {
const teamId = String(value || '');
setSelectedTeamIds(teamId ? [teamId] : []);
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do we store the team ids as an array. Seems like I can only select one at a time...

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.

3 participants