Skip to content
Merged
Show file tree
Hide file tree
Changes from 39 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
b50dc17
Add feature flag for production location page
VadimKovalenkoSNF Feb 5, 2026
78a34c2
Update release notes
VadimKovalenkoSNF Feb 5, 2026
398248f
Update facility routing
VadimKovalenkoSNF Feb 6, 2026
a779f4c
Fix redirect error with full path and os id mismatch
VadimKovalenkoSNF Feb 6, 2026
88de642
Add tests for util functions
VadimKovalenkoSNF Feb 9, 2026
719fe81
Upd release notes
VadimKovalenkoSNF Feb 9, 2026
e732986
Use normalizedOsId for proper data fetch
VadimKovalenkoSNF Feb 10, 2026
a817ff8
Fix migration conflicts, upd release notes
VadimKovalenkoSNF Feb 12, 2026
0906787
Upd release notes
VadimKovalenkoSNF Feb 12, 2026
192f773
Minor test update
VadimKovalenkoSNF Feb 12, 2026
2576d5a
Redirect to regular facility route im embed mode is embed mode is ena…
VadimKovalenkoSNF Feb 17, 2026
fda9275
Upd Release notes
VadimKovalenkoSNF Feb 17, 2026
daa3bc1
Fix Release notes
VadimKovalenkoSNF Feb 17, 2026
279ef5f
Add basic components for new production location page (partial impl)
VadimKovalenkoSNF Feb 19, 2026
de18a05
Restructure components
VadimKovalenkoSNF Feb 20, 2026
3c68834
Fix responsive layout
VadimKovalenkoSNF Feb 20, 2026
d89861a
Merge branch 'main' into OSDEV-2353-production-location-skeleton-page
VadimKovalenkoSNF Feb 23, 2026
3da0f66
Fix post-merge issues
VadimKovalenkoSNF Feb 23, 2026
b33a6c1
Fix mail link to point to the old facility endpoint
VadimKovalenkoSNF Feb 23, 2026
9c2b761
Fix move-to-facility link
VadimKovalenkoSNF Feb 23, 2026
b0aa20c
Add title object to classes
VadimKovalenkoSNF Feb 23, 2026
6f8340a
Fix Redirect condition when production location has been merged
VadimKovalenkoSNF Feb 23, 2026
a76e643
Add a title entry to contributeFieldsStyles
VadimKovalenkoSNF Feb 23, 2026
dfcc4da
Use proper links in ContributeFields
VadimKovalenkoSNF Feb 23, 2026
852ced3
Replace href with to on every Link
VadimKovalenkoSNF Feb 23, 2026
8104e25
Minor refactoring of the closure status
VadimKovalenkoSNF Feb 23, 2026
81d1bd7
Introduce getPrimaryText() function
VadimKovalenkoSNF Feb 23, 2026
b6328ab
Merge branch 'main' into OSDEV-2353-production-location-skeleton-page
VadimKovalenkoSNF Feb 23, 2026
c72ddb5
Fix lint issues
VadimKovalenkoSNF Feb 23, 2026
5262030
Minor lint fixes, add missing style
VadimKovalenkoSNF Feb 24, 2026
6530fa9
Merge branch 'main' into OSDEV-2353-production-location-skeleton-page
VadimKovalenkoSNF Feb 24, 2026
05e0eaf
Restructure folders and components related to new Production Location…
VadimKovalenkoSNF Feb 24, 2026
f06cff5
Rename components filenames to their corresponding names
VadimKovalenkoSNF Feb 24, 2026
c3be47b
Remove index.js entry point for production location components
VadimKovalenkoSNF Feb 24, 2026
8593157
Fix lint issues
VadimKovalenkoSNF Feb 24, 2026
74432e8
Add safeguard for closure report
VadimKovalenkoSNF Feb 24, 2026
74ef0a1
Create separate PrimaryText component
VadimKovalenkoSNF Feb 24, 2026
4089af0
Fix lint issue
VadimKovalenkoSNF Feb 24, 2026
1e64dae
Update release notes
VadimKovalenkoSNF Feb 24, 2026
8f3e3f1
Update release notes placement
VadimKovalenkoSNF Feb 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/release/RELEASE-NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html
* Added support for displaying nested objects, integer, date, and date-time properties in partner fields with JSON schema. Updated system partner field constraints to allow modifications to inactive partner fields through the Django admin panel, enabling safe updates while maintaining data integrity for active fields.
* Improved CKEditor integration by automatically cleaning empty placeholder content (`<p>&nbsp;</p>`) from rich text fields on save, preventing meaningless HTML from being stored in the database.
* Fixed styling for nested HTML elements in partner field source descriptions to ensure consistent margins and padding across all nested tags.
* [OSDEV-2353](https://opensupplyhub.atlassian.net/browse/OSDEV-2353) - Created basic layout components for new Productino Location page redesign.

### Release instructions
* Ensure that the following commands are included in the `post_deployment` command:
Expand Down
9 changes: 7 additions & 2 deletions src/django/api/mail.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,13 @@ def make_claim_url(request: Request, location: Facility):
# New claim flow: /claim/{os_id}.
return '{}/claim/{}'.format(make_oshub_url(request), location.id)
else:
# Old claim flow: /facilities/{os_id}/claim.
return '{}/claim'.format(make_facility_url(request, location))
# Old claim flow: /facilities/{os_id}/claim
# (do not use make_facility_url
# so we never produce /production-locations/{id}/claim when
# PRODUCTION_LOCATION_PAGE_SWITCH is on).
return '{}/facilities/{}/claim'.format(
make_oshub_url(request), location.id
)


def make_pl_search_url(request):
Expand Down
4 changes: 2 additions & 2 deletions src/react/src/Routes.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import SurveyDialogNotification from './components/SurveyDialogNotification';
import Settings from './components/Settings/Settings';
import ExternalRedirect from './components/ExternalRedirect';
import Facilities from './components/Facilities';
import ProductionLocationDetails from './components/ProductionLocation/ProductionLocationDetails';
import ProductionLocationDetailsContainer from './components/ProductionLocation/ProductionLocationDetailsContainer/ProductionLocationDetailsContainer';
import ContributeProductionLocation from './components/Contribute/ContributeProductionLocation';
import SearchByOsIdResult from './components/Contribute/SearchByOsIdResult';
import SearchByNameAndAddressResult from './components/Contribute/SearchByNameAndAddressResult';
Expand Down Expand Up @@ -165,7 +165,7 @@ class Routes extends Component {
>
<Route
component={
ProductionLocationDetails
ProductionLocationDetailsContainer
}
/>
</FeatureFlag>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React from 'react';
import { withStyles } from '@material-ui/core/styles';
import Typography from '@material-ui/core/Typography';

import styles from './styles';

const ClaimDataContainer = ({ classes, className }) => (
<div className={`${classes.container} ${className || ''}`}>
<Typography variant="title" className={classes.title} component="h3">
Claim Data
</Typography>
</div>
);

export default withStyles(styles)(ClaimDataContainer);
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export default theme =>
Object.freeze({
container: Object.freeze({
backgroundColor: 'white',
}),
title: Object.freeze({
marginBottom: theme.spacing.unit,
}),
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import React from 'react';
import { Link } from 'react-router-dom';
import { connect } from 'react-redux';
import Typography from '@material-ui/core/Typography';
import Icon from '@material-ui/core/Icon';
import { withStyles } from '@material-ui/core/styles';

import BadgeClaimed from '../../../BadgeClaimed';

import {
makeClaimFacilityLinkWithFeatureFlag,
convertFeatureFlagsObjectToListOfActiveFlags,
} from '../../../../util/util';
import { ENABLE_V1_CLAIMS_FLOW } from '../../../../util/constants';

import { getBackgroundColorClass, getMainText } from './utils';
import styles from './styles';

const FacilityDetailsClaimFlag = ({
classes,
osId,
isClaimed,
isPending,
isEmbed,
isV1ClaimsFlowEnabled,
}) => {
if (isEmbed) return null;
const rootVariantClass =
classes[getBackgroundColorClass(isClaimed, isPending)];
const claimFacilityLink = makeClaimFacilityLinkWithFeatureFlag(
osId,
isV1ClaimsFlowEnabled,
);
return (
<div className={rootVariantClass} data-testid="claim-banner">
<div className={classes.contentContainer}>
<Icon className={classes.itemPadding}>
<BadgeClaimed />
</Icon>
<Typography className={classes.itemPadding}>
{getMainText(isClaimed, isPending)}
</Typography>
{!isClaimed && !isPending ? (
<Typography className={classes.itemPadding}>
<Link
to={claimFacilityLink}
href={claimFacilityLink}
className={classes.link}
>
I want to claim this production location
</Link>
</Typography>
) : null}
</div>
</div>
);
};

const mapStateToProps = ({ featureFlags: { flags } }) => {
const activeFeatureFlags = convertFeatureFlagsObjectToListOfActiveFlags(
flags,
);
return {
isV1ClaimsFlowEnabled: activeFeatureFlags.includes(
ENABLE_V1_CLAIMS_FLOW,
),
};
};

export default connect(mapStateToProps)(
withStyles(styles)(FacilityDetailsClaimFlag),
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import COLOURS from '../../../../util/COLOURS';

export default theme =>
Object.freeze({
rootClaimed: {
backgroundColor: COLOURS.GREEN,
color: '#191919',
display: 'flex',
justifyContent: 'center',
},
rootPending: {
backgroundColor: COLOURS.NAVIGATION,
color: '#191919',
display: 'flex',
justifyContent: 'center',
},
rootUnclaimed: {
backgroundColor: COLOURS.LIGHT_RED,
color: '#191919',
display: 'flex',
justifyContent: 'center',
},
contentContainer: {
width: '100%',
maxWidth: '1072px',
display: 'flex',
alignItems: 'center',
flexWrap: 'wrap',
paddingRight: theme.spacing.unit,
paddingBottom: theme.spacing.unit,
},
link: {
color: theme.palette.primary.main,
},
itemPadding: {
paddingLeft: theme.spacing.unit * 3,
paddingTop: theme.spacing.unit,
paddingBottom: theme.spacing.unit / 4,
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export const getBackgroundColorClass = (isClaimed, isPending) => {
if (isClaimed) return 'rootClaimed';
if (isPending) return 'rootPending';
return 'rootUnclaimed';
};

export const getMainText = (isClaimed, isPending) => {
if (isClaimed) {
return 'This production location has been claimed by an owner or manager';
}
if (isPending) {
return 'There is a pending claim for this production location';
}
return 'This production location has not been claimed';
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import React from 'react';
import get from 'lodash/get';
import Typography from '@material-ui/core/Typography';
import { withStyles } from '@material-ui/core/styles';

import FeatureFlag from '../../../FeatureFlag';
import { REPORT_A_FACILITY } from '../../../../util/constants';

import PrimaryText from './PrimaryText';
import styles from './styles';

const ProductionLocationDetailClosureStatus = ({
data,
clearFacility,
classes,
useProductionLocationPage = false,
search = '',
}) => {
const report = get(data, 'properties.activity_reports[0]');
const newOsId = get(data, 'properties.new_os_id');
const isClosed = get(data, 'properties.is_closed');
const isPending = report?.status === 'PENDING';

if (!report) return null;

if (!isPending && !isClosed) return null;

return (
<FeatureFlag flag={REPORT_A_FACILITY}>
<div className={classes.status}>
<div className={classes.contentContainer}>
<i
className={`${classes.text} ${classes.icon} far fa-fw fa-store-slash`}
/>
<div className={classes.textBox}>
<PrimaryText
report={report}
isPending={isPending}
isClosed={isClosed}
newOsId={newOsId}
classes={classes}
useProductionLocationPage={
useProductionLocationPage
}
search={search}
clearFacility={clearFacility}
/>
{isPending && (
<Typography
className={classes.text}
variant="body1"
>
Status pending
</Typography>
)}
</div>
</div>
</div>
</FeatureFlag>
);
};

export default withStyles(styles)(ProductionLocationDetailClosureStatus);
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import React from 'react';
import Typography from '@material-ui/core/Typography';
import { Link } from 'react-router-dom';

import {
makeFacilityDetailLink,
makeFacilityDetailLinkOnRedirect,
} from '../../../../util/util';

const PrimaryText = ({
report,
isPending,
isClosed,
newOsId,
classes,
useProductionLocationPage,
search,
clearFacility,
}) => {
if (isPending) {
const closureState = (report.closure_state || 'unknown').toLowerCase();
return (
<Typography className={classes.text} variant="subheading">
This facility may be {closureState}
</Typography>
);
}

if (isClosed && !!newOsId) {
const movedToPathname = useProductionLocationPage
? makeFacilityDetailLinkOnRedirect(
newOsId,
search,
useProductionLocationPage,
)
: makeFacilityDetailLink(newOsId);
return (
<Typography className={classes.text} variant="subheading">
This facility has moved to{' '}
<Link
to={{
pathname: movedToPathname,
state: {
panMapToFacilityDetails: true,
},
}}
className={classes.text}
onClick={() => clearFacility()}
>
{newOsId}
</Link>
</Typography>
);
}

if (isClosed) {
return (
<Typography className={classes.text} variant="subheading">
This facility is closed
</Typography>
);
}

return null;
};

export default PrimaryText;
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
export default theme =>
Object.freeze({
status: {
backgroundColor: 'rgb(40, 39, 39)',
borderRadius: 0,
display: 'flex',
justifyContent: 'center',
},
contentContainer: {
width: '100%',
maxWidth: '1072px',
display: 'flex',
alignItems: 'center',
flexWrap: 'wrap',
padding: theme.spacing.unit,
},
icon: {
fontSize: '24px',
fontWeight: 'normal',
paddingLeft: theme.spacing.unit * 2,
paddingRight: theme.spacing.unit * 3,
},
textBox: {
display: 'flex',
flexDirection: 'column',
},
text: {
color: 'rgb(255, 255, 255)',
fontSize: '14px',
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React from 'react';
import Typography from '@material-ui/core/Typography';
import { withStyles } from '@material-ui/core/styles';

import styles from './styles';

const ProductionLocationDetailsDataSourcesInfo = ({ classes, className }) => (
<div className={`${classes.container} ${className || ''}`}>
<Typography variant="title" className={classes.title} component="h3">
Understanding Data Sources
</Typography>
</div>
);

export default withStyles(styles)(ProductionLocationDetailsDataSourcesInfo);
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export default theme =>
Object.freeze({
container: Object.freeze({
backgroundColor: 'white',
}),
title: Object.freeze({
marginBottom: theme.spacing.unit,
}),
});
Loading