Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
26 changes: 26 additions & 0 deletions app/src/__tests__/utils/navigationPermissions.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { canNavigateTo } from '@/utils/navigationPermissions';

describe('navigationPermissions', () => {
describe('canNavigateTo', () => {
it('should allow Operations to navigate to vendorAccount', () => {
expect(canNavigateTo('vendorAccount', 'Operations')).toBe(true);
});

it('should deny Vendor to navigate to vendorAccount', () => {
expect(canNavigateTo('vendorAccount', 'Vendor')).toBe(false);
});

it('should allow Client to navigate to buyer', () => {
expect(canNavigateTo('buyer', 'Client')).toBe(true);
});

it('should deny Vendor to navigate to clientAccount', () => {
expect(canNavigateTo('clientAccount', 'Vendor')).toBe(false);
});

it('should deny undefined accountType', () => {
expect(canNavigateTo('buyer', undefined)).toBe(false);
expect(canNavigateTo('clientAccount', undefined)).toBe(false);
});
});
});
2 changes: 2 additions & 0 deletions app/src/components/list-item/DetailsListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import ListItemWithLabelAndText from '@/components/list-item/ListItemWithLabelAn
import type { DetailsListItemProps } from '@/types/lists';

const DetailsListItem = ({ label, data, hideImage, isLast, onPress }: DetailsListItemProps) => {
const disabled = !onPress;
if (!data) {
return <ListItemWithLabelAndText title={label} subtitle={undefined} isLast={isLast} />;
}
Expand All @@ -16,6 +17,7 @@ const DetailsListItem = ({ label, data, hideImage, isLast, onPress }: DetailsLis
hideImage={hideImage}
imagePath={data.icon}
onPress={onPress}
disabled={disabled}
isLast={isLast}
/>
);
Expand Down
16 changes: 14 additions & 2 deletions app/src/components/list-item/ListItemWithImage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ type Props = {
isSelected?: boolean;
isUpdatingSelection?: boolean;
onPress?: () => void;
disabled?: boolean;
testID?: string;
};

Expand All @@ -29,9 +30,16 @@ const ListItemWithImage = ({
isSelected,
isUpdatingSelection,
onPress,
disabled,
testID,
}: Props) => (
<TouchableOpacity testID={testID} style={styles.container} onPress={onPress} activeOpacity={0.7}>
<TouchableOpacity
testID={testID}
style={styles.container}
onPress={onPress}
disabled={disabled || !onPress}
activeOpacity={0.7}
>
<View style={[styles.contentWrapper, isLast && styles.lastItem]}>
{!hideImage && (
<View style={styles.avatarWrapper}>
Expand All @@ -43,7 +51,10 @@ const ListItemWithImage = ({
{title}
</Text>
<Text
style={subtitleLink ? styles.subtitleLink : styles.subtitle}
style={[
subtitleLink ? styles.subtitleLink : styles.subtitle,
disabled && styles.disabledSubtitle,
]}
numberOfLines={1}
ellipsizeMode="tail"
>
Expand Down Expand Up @@ -71,6 +82,7 @@ const styles = StyleSheet.create({
title: listItemStyle.title,
subtitle: listItemStyle.textAndImage.subtitle,
subtitleLink: linkStyle.listItemLinkRegular,
disabledSubtitle: listItemStyle.disabled.subtitle,
});

export default ListItemWithImage;
37 changes: 25 additions & 12 deletions app/src/screens/agreements/AgreementDetailsContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,21 @@ import CardWithHeader from '@/components/card/CardWithHeader';
import DetailsListItem from '@/components/list-item/DetailsListItem';
import ListItemWithLabelAndText from '@/components/list-item/ListItemWithLabelAndText';
import { EMPTY_VALUE } from '@/constants/common';
import { useAccount } from '@/context/AccountContext';
import type { AgreementData } from '@/types/agreement';
import type { AccountType } from '@/types/common';
import type { RootStackParamList } from '@/types/navigation';
import { formatNumber, formatPercentage } from '@/utils/formatting';
import { calculateMarginWithMarkup } from '@/utils/formulas';
import { canNavigateTo } from '@/utils/navigationPermissions';

const AgreementDetailsContent = ({ data }: { data: AgreementData }) => {
const { t, i18n } = useTranslation();
const language = i18n.language;

const navigation = useNavigation<StackNavigationProp<RootStackParamList>>();
const { userData } = useAccount();
const accountType = userData?.currentAccount?.type as AccountType | undefined;

const hasBillingCurrencyData = data.price?.billingCurrency;
const labelMonth = `${data.price?.currency}/${t('details.month')}`;
Expand All @@ -42,12 +47,16 @@ const AgreementDetailsContent = ({ data }: { data: AgreementData }) => {
<DetailsListItem
label={t(`details.vendor`)}
data={data.vendor}
onPress={() => {
navigation.navigate('accountDetails', {
id: data.vendor?.id,
type: 'vendor',
});
}}
onPress={
canNavigateTo('vendorAccount', accountType)
? () => {
navigation.navigate('accountDetails', {
id: data.vendor?.id,
type: 'vendor',
});
}
: undefined
}
/>
<DetailsListItem
label={t(`details.product`)}
Expand All @@ -65,12 +74,16 @@ const AgreementDetailsContent = ({ data }: { data: AgreementData }) => {
<DetailsListItem
label={t(`details.client`)}
data={data.client}
onPress={() => {
navigation.navigate('accountDetails', {
id: data.client?.id,
type: 'client',
});
}}
onPress={
canNavigateTo('clientAccount', accountType)
? () => {
navigation.navigate('accountDetails', {
id: data.client?.id,
type: 'client',
});
}
: undefined
}
/>
<ListItemWithLabelAndText title={t(`details.ppx`)} subtitle={`${PPxM} ${PPxY}`} />
<ListItemWithLabelAndText
Expand Down
21 changes: 15 additions & 6 deletions app/src/screens/buyers/BuyerDetailsContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,35 @@ import AddressCard from '@/components/address/AddressCard';
import CardWithHeader from '@/components/card/CardWithHeader';
import DetailsListItem from '@/components/list-item/DetailsListItem';
import ListItemWithLabelAndText from '@/components/list-item/ListItemWithLabelAndText';
import { useAccount } from '@/context/AccountContext';
import type { BuyerData } from '@/types/admin';
import type { AccountType } from '@/types/common';
import type { RootStackParamList } from '@/types/navigation';
import { canNavigateTo } from '@/utils/navigationPermissions';

const BuyerDetailsContent = ({ data }: { data: BuyerData }) => {
const { t } = useTranslation();

const navigation = useNavigation<StackNavigationProp<RootStackParamList>>();
const { userData } = useAccount();
const accountType = userData?.currentAccount?.type as AccountType | undefined;

return (
<>
<CardWithHeader title={t(`details.title`)}>
<DetailsListItem
label={t(`details.account`)}
data={data.account}
onPress={() => {
navigation.navigate('accountDetails', {
id: data.account?.id,
type: 'client',
});
}}
onPress={
canNavigateTo('clientAccount', accountType)
? () => {
navigation.navigate('accountDetails', {
id: data.account?.id,
type: 'client',
});
}
: undefined
}
/>

<ListItemWithLabelAndText
Expand Down
20 changes: 20 additions & 0 deletions app/src/screens/buyers/BuyersScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,15 @@ import { useTranslation } from 'react-i18next';
import StatusMessage from '@/components/common/EmptyStateHelper';
import { ListView } from '@/components/list/ListView';
import { listItemConfigFull } from '@/config/list';
import { useAccount } from '@/context/AccountContext';
import { useBuyers, BuyersProvider } from '@/context/BuyersContext';
import type { RootStackParamList } from '@/types/navigation';
import { TestIDs } from '@/utils/testID';

const BuyersScreenContent = () => {
const { userData } = useAccount();
const accountType = userData?.currentAccount?.type;

const {
buyers,
isBuyersLoading,
Expand All @@ -24,6 +28,22 @@ const BuyersScreenContent = () => {

const navigation = useNavigation<StackNavigationProp<RootStackParamList>>();

if (accountType === 'Vendor') {
return (
<StatusMessage
isLoading={false}
isError={false}
isEmpty={true}
isUnauthorised={false}
emptyTestId={TestIDs.BUYERS_EMPTY_STATE}
emptyTitle={t('buyersScreen.emptyStateTitle')}
emptyDescription={t('buyersScreen.emptyStateDescription')}
>
{null}
</StatusMessage>
);
}

return (
<StatusMessage
isLoading={isBuyersLoading}
Expand Down
36 changes: 25 additions & 11 deletions app/src/screens/certificates/CertificateDetailsContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,21 @@ import { useTranslation } from 'react-i18next';
import CardWithHeader from '@/components/card/CardWithHeader';
import DetailsListItem from '@/components/list-item/DetailsListItem';
import ListItemWithLabelAndText from '@/components/list-item/ListItemWithLabelAndText';
import { useAccount } from '@/context/AccountContext';
import type { AccountType } from '@/types/common';
import type { RootStackParamList } from '@/types/navigation';
import type { CertificateDetails } from '@/types/program';
import { formatDateForLocale } from '@/utils/formatting';
import { canNavigateTo } from '@/utils/navigationPermissions';

const CertificateDetailsContent = ({ data }: { data: CertificateDetails }) => {
const { t, i18n } = useTranslation();

const language = i18n.language;

const navigation = useNavigation<StackNavigationProp<RootStackParamList>>();
const { userData } = useAccount();
const accountType = userData?.currentAccount?.type as AccountType | undefined;

const eligibility = data.eligibility.partner
? t('details.eligibilityValue.partner')
Expand All @@ -36,21 +41,30 @@ const CertificateDetailsContent = ({ data }: { data: CertificateDetails }) => {
<DetailsListItem
label={t(`details.vendor`)}
data={data.vendor}
onPress={() => {
navigation.navigate('accountDetails', {
id: data.vendor?.id,
type: 'vendor',
});
}}
onPress={
canNavigateTo('vendorAccount', accountType)
? () => {
navigation.navigate('accountDetails', {
id: data.vendor?.id,
type: 'vendor',
});
}
: undefined
}
/>
{/* TODO: certificant can be also a Licensee */}
<DetailsListItem
label={t(`details.certificant`)}
data={data.buyer}
onPress={() => {
navigation.navigate('buyerDetails', {
id: data.buyer?.id,
});
}}
onPress={
canNavigateTo('buyer', accountType)
? () => {
navigation.navigate('buyerDetails', {
id: data.buyer?.id,
});
}
: undefined
}
/>
<ListItemWithLabelAndText title={t('details.eligibility')} subtitle={eligibility} />
<ListItemWithLabelAndText
Expand Down
19 changes: 14 additions & 5 deletions app/src/screens/invoices/InvoiceDetailsContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,21 @@ import CardWithHeader from '@/components/card/CardWithHeader';
import DetailsListItem from '@/components/list-item/DetailsListItem';
import ListItemWithLabelAndText from '@/components/list-item/ListItemWithLabelAndText';
import { EMPTY_VALUE } from '@/constants/common';
import { useAccount } from '@/context/AccountContext';
import type { InvoiceDetails } from '@/types/billing';
import type { AccountType } from '@/types/common';
import type { RootStackParamList } from '@/types/navigation';
import { formatDateForLocale, formatNumber } from '@/utils/formatting';
import { canNavigateTo } from '@/utils/navigationPermissions';

const InvoiceDetailsContent = ({ data }: { data: InvoiceDetails }) => {
const { t, i18n } = useTranslation();

const language = i18n.language;

const navigation = useNavigation<StackNavigationProp<RootStackParamList>>();
const { userData } = useAccount();
const accountType = userData?.currentAccount?.type as AccountType | undefined;

const totalSP = formatNumber(data.price.totalSP, 2, language) || EMPTY_VALUE;
const totalGT = formatNumber(data.price.totalGT, 2, language) || EMPTY_VALUE;
Expand All @@ -37,11 +42,15 @@ const InvoiceDetailsContent = ({ data }: { data: InvoiceDetails }) => {
<DetailsListItem
label={t(`details.owner`)}
data={data.statement?.ledger?.owner}
onPress={() => {
navigation.navigate('sellerDetails', {
id: data.statement?.ledger?.owner?.id,
});
}}
onPress={
canNavigateTo('seller', accountType)
? () => {
navigation.navigate('sellerDetails', {
id: data.statement?.ledger?.owner?.id,
});
}
: undefined
}
/>
<ListItemWithLabelAndText
title={t(`details.sp`)}
Expand Down
Loading