Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
b04fbdf
feat: Add Strictly enforce workspace rules toggle
jakubstec Mar 13, 2026
0b5b677
fix: add margin between toggle description and error in group settings
jakubstec Mar 13, 2026
585789f
Revert "fix: add margin between toggle description and error in group…
jakubstec Mar 13, 2026
22a49c1
fix: change styling to match design doc (section dividier, html error…
jakubstec Mar 13, 2026
0c3a786
chore: revert accidental ios folder changes
jakubstec Mar 16, 2026
89527f6
Merge branch 'war-in/domains/security-group-details-page' into jakubs…
jakubstec Mar 16, 2026
2db1ec3
Merge remote-tracking branch 'origin/war-in/domains/security-group-de…
jakubstec Apr 24, 2026
0ca7f42
minor fix after merge commit
jakubstec Apr 24, 2026
8ad3faf
Merge remote-tracking branch 'origin/main' into jakubstec/domains/sec…
jakubstec Apr 24, 2026
bea28b2
fix: prettier
jakubstec Apr 24, 2026
824ac9e
fix: typecheck
jakubstec Apr 24, 2026
6e7c6f6
fix: add offline feedback to security group row
jakubstec Apr 24, 2026
81c5fb2
fix: add missing translations
jakubstec Apr 24, 2026
0c44cd8
fix: remove claude file
jakubstec Apr 24, 2026
89897be
fix: add null fallback to failureData rollback
jakubstec Apr 28, 2026
b4c4704
fix: change onToggle fallback
jakubstec Apr 28, 2026
d18ea6b
Merge remote-tracking branch 'origin/main' into jakubstec/domains/sec…
jakubstec Apr 29, 2026
032918e
Merge remote-tracking branch 'origin/main' into jakubstec/domains/sec…
jakubstec Apr 30, 2026
171e02b
fix: prettier
jakubstec Apr 30, 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
3 changes: 3 additions & 0 deletions src/languages/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9156,6 +9156,9 @@ Hier ist ein *Testbeleg*, um dir zu zeigen, wie es funktioniert:`,
`Möchtest du ${newName} wirklich zur Standardgruppe machen? Neue Mitglieder werden zu dieser Gruppe anstelle der bisherigen Standardgruppe (${currentName}) eingeladen. `,
makeDefault: 'Als Standard festlegen',
neverMind: 'Vergiss es',
permissions: 'Gruppenberechtigungen',
StrictlyEnforceWorkspaceRules: 'Workspace-Regeln strikt durchsetzen',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

You can change this in future pr, but I agree the keys should start lowecase

Suggested change
StrictlyEnforceWorkspaceRules: 'Workspace-Regeln strikt durchsetzen',
strictlyEnforceWorkspaceRules: 'Workspace-Regeln strikt durchsetzen',

StrictlyEnforceWorkspaceRulesDescription: 'Alle Workspace-Regeln müssen erfüllt sein, bevor ein Bericht eingereicht wird. Manuelle Ausnahmen sind nicht zulässig.',
},
},
proactiveAppReview: {
Expand Down
3 changes: 3 additions & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9147,6 +9147,9 @@ const translations = {
`Are you sure you want to make ${newName} the default group? New members will be invited to this group instead of the previous default group (${currentName}). `,
makeDefault: 'Make default',
neverMind: 'Never mind',
permissions: 'Group permissions',
StrictlyEnforceWorkspaceRules: 'Strictly enforce workspace rules',
StrictlyEnforceWorkspaceRulesDescription: 'All workspace rules must be met before submitting a report. No manual exceptions allowed.',
},
},
};
Expand Down
3 changes: 3 additions & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9378,6 +9378,9 @@ ${amount} para ${merchant} - ${date}`,
`¿Estás seguro de que quieres establecer ${newName} como el grupo predeterminado? Los nuevos miembros serán invitados a este grupo en lugar del grupo predeterminado anterior (${currentName}). `,
makeDefault: 'Establecer como predeterminado',
neverMind: 'No importa',
permissions: 'Permisos de grupo',
StrictlyEnforceWorkspaceRules: 'Hacer cumplir estrictamente las reglas del espacio de trabajo',
StrictlyEnforceWorkspaceRulesDescription: 'Todas las reglas del espacio de trabajo deben cumplirse antes de enviar un informe. No se permiten excepciones manuales.',
},
},
gps: {
Expand Down
4 changes: 4 additions & 0 deletions src/languages/fr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9179,6 +9179,10 @@ Voici un *reçu test* pour vous montrer comment ça fonctionne :`,
`Êtes-vous sûr de vouloir faire de ${newName} le groupe par défaut ? Les nouveaux membres seront invités à ce groupe au lieu du groupe par défaut précédent (${currentName}). `,
makeDefault: 'Définir par défaut',
neverMind: 'Peu importe',
permissions: 'Autorisations du groupe',
StrictlyEnforceWorkspaceRules: 'Appliquer strictement les règles de l’espace de travail',
StrictlyEnforceWorkspaceRulesDescription:
'Toutes les règles de l’espace de travail doivent être respectées avant de soumettre un rapport. Aucune exception manuelle n’est autorisée.',
},
},
proactiveAppReview: {
Expand Down
3 changes: 3 additions & 0 deletions src/languages/it.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9146,6 +9146,9 @@ Ecco una *ricevuta di prova* per mostrarti come funziona:`,
`Sei sicuro di voler impostare ${newName} come gruppo predefinito? I nuovi membri verranno invitati a questo gruppo invece del precedente gruppo predefinito (${currentName}). `,
makeDefault: 'Imposta come predefinito',
neverMind: 'Lascia perdere',
permissions: 'Autorizzazioni del gruppo',
StrictlyEnforceWorkspaceRules: 'Applica rigorosamente le regole dello spazio di lavoro',
StrictlyEnforceWorkspaceRulesDescription: 'Tutte le regole dello spazio di lavoro devono essere soddisfatte prima di inviare un rapporto. Non sono consentite eccezioni manuali.',
},
},
proactiveAppReview: {
Expand Down
3 changes: 3 additions & 0 deletions src/languages/ja.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9032,6 +9032,9 @@ ${reportName}
`本当に ${newName} をデフォルトグループに設定しますか?新しいメンバーは、以前のデフォルトグループ (${currentName}) ではなく、このグループに招待されます。`,
makeDefault: 'デフォルトに設定',
neverMind: 'やめておく',
permissions: 'グループの権限',
StrictlyEnforceWorkspaceRules: 'ワークスペースのルールを厳密に適用する',
StrictlyEnforceWorkspaceRulesDescription: 'レポートを送信する前にすべてのワークスペースのルールを満たす必要があります。手動による例外は許可されていません。',
},
},
proactiveAppReview: {
Expand Down
4 changes: 4 additions & 0 deletions src/languages/nl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9118,6 +9118,10 @@ Hier is een *proefbon* om je te laten zien hoe het werkt:`,
`Weet je zeker dat je ${newName} de standaardgroep wilt maken? Nieuwe leden worden uitgenodigd voor deze groep in plaats van de vorige standaardgroep (${currentName}). `,
makeDefault: 'Standaard maken',
neverMind: 'Laat maar',
permissions: 'Groepsmachtigingen',
StrictlyEnforceWorkspaceRules: 'Werkruimteregels strikt afdwingen',
StrictlyEnforceWorkspaceRulesDescription:
'Aan alle werkruimteregels moet worden voldaan voordat een rapport kan worden ingediend. Handmatige uitzonderingen zijn niet toegestaan.',
},
},
proactiveAppReview: {
Expand Down
3 changes: 3 additions & 0 deletions src/languages/pl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9100,6 +9100,9 @@ Oto *paragon testowy*, żeby pokazać Ci, jak to działa:`,
`Czy na pewno chcesz ustawić ${newName} jako grupę domyślną? Nowi członkowie będą zapraszani do tej grupy zamiast do poprzedniej grupy domyślnej (${currentName}). `,
makeDefault: 'Ustaw jako domyślną',
neverMind: 'Nieważne',
permissions: 'Uprawnienia grupy',
StrictlyEnforceWorkspaceRules: 'Ściśle egzekwuj reguły przestrzeni roboczej',
StrictlyEnforceWorkspaceRulesDescription: 'Przed przesłaniem raportu muszą zostać spełnione wszystkie reguły przestrzeni roboczej. Ręczne wyjątki są niedozwolone.',
},
},
proactiveAppReview: {
Expand Down
3 changes: 3 additions & 0 deletions src/languages/pt-BR.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9108,6 +9108,9 @@ Aqui está um *comprovante de teste* para mostrar como funciona:`,
`Tem certeza de que deseja tornar ${newName} o grupo padrão? Novos membros serão convidados para este grupo em vez do grupo padrão anterior (${currentName}). `,
makeDefault: 'Tornar padrão',
neverMind: 'Deixa pra lá',
permissions: 'Permissões do grupo',
StrictlyEnforceWorkspaceRules: 'Aplicar rigorosamente as regras do espaço de trabalho',
StrictlyEnforceWorkspaceRulesDescription: 'Todas as regras do espaço de trabalho devem ser atendidas antes de enviar um relatório. Não são permitidas exceções manuais.',
},
},
proactiveAppReview: {
Expand Down
3 changes: 3 additions & 0 deletions src/languages/zh-hans.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8873,6 +8873,9 @@ ${reportName}
defaultGroupPrompt: (currentName: string, newName: string) => `您确定要将 ${newName} 设为默认群组吗?新成员将被邀请加入此群组,而不是之前的默认群组(${currentName})。`,
makeDefault: '设为默认',
neverMind: '算了',
permissions: '群组权限',
StrictlyEnforceWorkspaceRules: '严格执行工作空间规则',
StrictlyEnforceWorkspaceRulesDescription: '提交报告前必须满足所有工作空间规则。不允许手动例外。',
},
},
proactiveAppReview: {title: '喜欢全新的 Expensify 吗?', description: '请告诉我们,这样我们就能帮助您让报销体验变得更好。', positiveButton: '太棒了!', negativeButton: '不太是'},
Expand Down
6 changes: 3 additions & 3 deletions src/libs/actions/Domain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1836,7 +1836,7 @@ function updateDomainSecurityGroup(
groupID: string,
currentSecurityGroup: DomainSecurityGroup,
newSettingValue: Partial<DomainSecurityGroup>,
settingsName: keyof Pick<DomainSecurityGroup, 'name'>,
settingsName: keyof Pick<DomainSecurityGroup, 'name' | 'enableStrictPolicyRules'>,
Comment thread
mountiny marked this conversation as resolved.
) {
const SECURITY_GROUP_KEY = `${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}${groupID}`;
const newSecurityGroup = {...currentSecurityGroup, ...newSettingValue};
Expand Down Expand Up @@ -1879,9 +1879,9 @@ function updateDomainSecurityGroup(
key: `${ONYXKEYS.COLLECTION.DOMAIN}${domainAccountID}`,
value: {
[SECURITY_GROUP_KEY]: {
[settingsName]: currentSecurityGroup[settingsName],
[settingsName]: currentSecurityGroup[settingsName] ?? null,
},
} as PrefixedRecord<typeof CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX, DomainSecurityGroup>,
} as PrefixedRecord<typeof CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX, Partial<DomainSecurityGroup>>,
},
{
onyxMethod: Onyx.METHOD.MERGE,
Expand Down
11 changes: 10 additions & 1 deletion src/pages/domain/Groups/DomainGroupDetailsPage.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import {domainSecurityGroupSettingErrorsSelector, domainSecurityGroupSettingPendingActionSelector, selectGroupByID} from '@selectors/Domain';
import React from 'react';
import {View} from 'react-native';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription';
import OfflineWithFeedback from '@components/OfflineWithFeedback';
import ScreenWrapper from '@components/ScreenWrapper';
import ScrollView from '@components/ScrollView';
import Text from '@components/Text';
import useLocalize from '@hooks/useLocalize';
import useOnyx from '@hooks/useOnyx';
import useThemeStyles from '@hooks/useThemeStyles';
Expand All @@ -17,14 +19,15 @@ import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import type SCREENS from '@src/SCREENS';
import DefaultGroupToggle from './DefaultGroupToggle';
import StrictlyEnforceWorkspaceRulesToggle from './StrictlyEnforceWorkspaceRulesToggle';

type DomainGroupDetailsPageProps = PlatformStackScreenProps<SettingsNavigatorParamList, typeof SCREENS.DOMAIN.GROUP_DETAILS>;

function DomainGroupDetailsPage({route}: DomainGroupDetailsPageProps) {
const styles = useThemeStyles();
const {domainAccountID, groupID} = route.params;

const {translate} = useLocalize();
const styles = useThemeStyles();

const [group] = useOnyx(`${ONYXKEYS.COLLECTION.DOMAIN}${domainAccountID}`, {
selector: selectGroupByID(groupID),
Expand Down Expand Up @@ -62,6 +65,12 @@ function DomainGroupDetailsPage({route}: DomainGroupDetailsPageProps) {
groupID={groupID}
groupName={group?.name}
/>
<View style={[styles.sectionDividerLine, styles.mh5, styles.mv6]} />
<Text style={[styles.textNormal, styles.textStrong, styles.ph5]}>{translate('domain.groups.permissions')}</Text>
<StrictlyEnforceWorkspaceRulesToggle
domainAccountID={domainAccountID}
groupID={groupID}
/>
</ScrollView>
</ScreenWrapper>
</DomainNotFoundPageWrapper>
Expand Down
56 changes: 56 additions & 0 deletions src/pages/domain/Groups/StrictlyEnforceWorkspaceRulesToggle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import {domainSecurityGroupSettingErrorsSelector, domainSecurityGroupSettingPendingActionSelector, selectGroupByID} from '@selectors/Domain';
import React from 'react';
import {View} from 'react-native';
import useLocalize from '@hooks/useLocalize';
import useOnyx from '@hooks/useOnyx';
import useThemeStyles from '@hooks/useThemeStyles';
import ToggleSettingOptionRow from '@pages/workspace/workflows/ToggleSettingsOptionRow';
import {clearDomainSecurityGroupSettingError, updateDomainSecurityGroup} from '@userActions/Domain';
import ONYXKEYS from '@src/ONYXKEYS';

type StrictlyEnforceWorkspaceRulesToggleProps = {
domainAccountID: number;
groupID: string;
Comment on lines +12 to +13

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

all these types should have docs

};

function StrictlyEnforceWorkspaceRulesToggle({domainAccountID, groupID}: StrictlyEnforceWorkspaceRulesToggleProps) {
const styles = useThemeStyles();
const {translate} = useLocalize();

const [group] = useOnyx(`${ONYXKEYS.COLLECTION.DOMAIN}${domainAccountID}`, {
selector: selectGroupByID(groupID),
});

const [enableStrictPolicyRulesPendingAction] = useOnyx(`${ONYXKEYS.COLLECTION.DOMAIN_PENDING_ACTIONS}${domainAccountID}`, {
selector: domainSecurityGroupSettingPendingActionSelector('enableStrictPolicyRules', groupID),
});
const [enableStrictPolicyRulesErrors] = useOnyx(`${ONYXKEYS.COLLECTION.DOMAIN_ERRORS}${domainAccountID}`, {
selector: domainSecurityGroupSettingErrorsSelector('enableStrictPolicyRulesErrors', groupID),
});

const isEnabled = !!group?.enableStrictPolicyRules;

return (
<View style={styles.mv3}>
<ToggleSettingOptionRow
title={translate('domain.groups.StrictlyEnforceWorkspaceRules')}
subtitle={translate('domain.groups.StrictlyEnforceWorkspaceRulesDescription')}
switchAccessibilityLabel={translate('domain.groups.StrictlyEnforceWorkspaceRules')}
shouldPlaceSubtitleBelowSwitch
isActive={isEnabled}
onToggle={(enabled) => {
if (!group) {
return;
}
updateDomainSecurityGroup(domainAccountID, groupID, group, {enableStrictPolicyRules: enabled}, 'enableStrictPolicyRules');
}}
wrapperStyle={[styles.ph5]}
pendingAction={enableStrictPolicyRulesPendingAction}
errors={enableStrictPolicyRulesErrors}
onCloseError={() => clearDomainSecurityGroupSettingError(domainAccountID, groupID, 'enableStrictPolicyRulesErrors')}
/>
</View>
);
}

export default StrictlyEnforceWorkspaceRulesToggle;
5 changes: 5 additions & 0 deletions src/types/onyx/DomainErrors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ type DomainSecurityGroupErrors = {
* Errors related to the default security group ID setting
*/
defaultSecurityGroupIDErrors?: OnyxCommon.Errors;

/**
* Errors related to the strictly enforce workspace rules setting
*/
enableStrictPolicyRulesErrors?: OnyxCommon.Errors;
};

/**
Expand Down
5 changes: 5 additions & 0 deletions src/types/onyx/DomainPendingActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ type DomainSecurityGroupPendingActions = {
*/
name?: OnyxCommon.PendingAction;

/**
* Pending action for the strictly enforce workspace rules setting
*/
enableStrictPolicyRules?: OnyxCommon.PendingAction;

/**
* Pending action for the default security group ID
*/
Expand Down
Loading