Skip to content

Commit 6a72a63

Browse files
authored
Merge pull request Expensify#87684 from ishpaul777/ishpaul/workspace-rules-document-upload
[Payment due @abzokhattab] Add workspace rules PDF document upload, view, and management UI
2 parents 3c9b08b + 5ecf62a commit 6a72a63

32 files changed

Lines changed: 477 additions & 34 deletions

File tree

src/CONST/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9314,6 +9314,7 @@ const CONST = {
93149314
PLAN_TYPE: 'WorkspaceOverview-PlanType',
93159315
SHARE: 'WorkspaceOverview-Share',
93169316
CUSTOM_RULES: 'WorkspaceOverview-CustomRules',
9317+
RULES_DOCUMENT: 'WorkspaceOverview-RulesDocument',
93179318
INVITE_BUTTON: 'WorkspaceOverview-InviteButton',
93189319
MORE_DROPDOWN: 'WorkspaceOverview-MoreDropdown',
93199320
},

src/ROUTES.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2138,6 +2138,10 @@ const ROUTES = {
21382138
route: 'workspaces/:policyID/avatar',
21392139
getRoute: (policyID: string, fallbackLetter?: UpperCaseCharacters) => `workspaces/${policyID}/avatar${fallbackLetter ? `?letter=${fallbackLetter}` : ''}` as const,
21402140
},
2141+
WORKSPACE_DOCUMENT: {
2142+
route: 'workspaces/:policyID/document',
2143+
getRoute: (policyID: string) => `workspaces/${policyID}/document` as const,
2144+
},
21412145
WORKSPACE_JOIN_USER: {
21422146
route: 'workspaces/:policyID/join',
21432147
getRoute: (policyID: string, inviterEmail: string) => `workspaces/${policyID}/join?email=${inviterEmail}` as const,

src/SCREENS.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ const SCREENS = {
1919
REPORT: 'Report',
2020
PROFILE_AVATAR: 'ProfileAvatar',
2121
WORKSPACE_AVATAR: 'WorkspaceAvatar',
22+
WORKSPACE_DOCUMENT: 'WorkspaceDocument',
2223
REPORT_AVATAR: 'ReportAvatar',
2324
NOT_FOUND: 'not-found',
2425
TRANSITION_BETWEEN_APPS: 'TransitionBetweenApps',

src/components/AttachmentPicker/index.native.tsx

Lines changed: 64 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,25 @@ import type IconAsset from '@src/types/utils/IconAsset';
2727
import launchCamera from './launchCamera/launchCamera';
2828
import type AttachmentPickerProps from './types';
2929

30+
const EXTENSION_TO_NATIVE_TYPE: Record<string, string> = {
31+
pdf: String(types.pdf),
32+
doc: String(types.doc),
33+
docx: String(types.docx),
34+
zip: String(types.zip),
35+
txt: String(types.plainText),
36+
json: String(types.json),
37+
xls: String(types.xls),
38+
xlsx: String(types.xlsx),
39+
jpg: String(types.images),
40+
jpeg: String(types.images),
41+
png: String(types.images),
42+
gif: String(types.images),
43+
heif: String(types.images),
44+
heic: String(types.images),
45+
tif: String(types.images),
46+
tiff: String(types.images),
47+
};
48+
3049
type LocalCopy = {
3150
name: string | null;
3251
uri: string;
@@ -121,8 +140,10 @@ function AttachmentPicker({
121140
shouldHideCameraOption = false,
122141
shouldValidateImage = true,
123142
shouldHideGalleryOption = false,
143+
acceptedFileTypes,
124144
fileLimit = 1,
125145
onOpenPicker,
146+
shouldSkipAttachmentTypeModal = false,
126147
}: AttachmentPickerProps) {
127148
const icons = useMemoizedLazyExpensifyIcons(['Camera', 'Gallery', 'Paperclip']);
128149
const styles = useThemeStyles();
@@ -251,8 +272,22 @@ function AttachmentPicker({
251272
* Launch the DocumentPicker. Results are in the same format as ImagePicker
252273
*/
253274
const showDocumentPicker = useCallback(async (): Promise<LocalCopy[]> => {
275+
let pickerTypes: string[];
276+
if (acceptedFileTypes && acceptedFileTypes.length > 0) {
277+
const mappedTypes = acceptedFileTypes.reduce<string[]>((result, extension) => {
278+
const nativeType = EXTENSION_TO_NATIVE_TYPE[String(extension)];
279+
if (nativeType !== undefined && !result.includes(nativeType)) {
280+
result.push(nativeType);
281+
}
282+
return result;
283+
}, []);
284+
pickerTypes = mappedTypes.length > 0 ? mappedTypes : [types.allFiles];
285+
} else {
286+
pickerTypes = [type === CONST.ATTACHMENT_PICKER_TYPE.IMAGE ? types.images : types.allFiles];
287+
}
288+
254289
const pickedFiles = await pick({
255-
type: [type === CONST.ATTACHMENT_PICKER_TYPE.IMAGE ? types.images : types.allFiles],
290+
type: pickerTypes,
256291
allowMultiSelection: fileLimit !== 1,
257292
});
258293

@@ -280,7 +315,7 @@ function AttachmentPicker({
280315
type: file.type,
281316
};
282317
});
283-
}, [fileLimit, type]);
318+
}, [acceptedFileTypes, fileLimit, type]);
284319

285320
const menuItemData: Item[] = useMemo(() => {
286321
const data: Item[] = [
@@ -336,19 +371,6 @@ function AttachmentPicker({
336371
[showGeneralAlert, showImageCorruptionAlert, translate],
337372
);
338373

339-
/**
340-
* Opens the attachment modal
341-
*
342-
* @param onPickedHandler A callback that will be called with the selected attachment
343-
* @param onCanceledHandler A callback that will be called without a selected attachment
344-
*/
345-
const open = (onPickedHandler: (files: FileObject[]) => void, onCanceledHandler: () => void = () => {}, onClosedHandler: () => void = () => {}) => {
346-
completeAttachmentSelection.current = onPickedHandler;
347-
onCanceled.current = onCanceledHandler;
348-
onClosed.current = onClosedHandler;
349-
setIsVisible(true);
350-
};
351-
352374
/**
353375
* Closes the attachment modal
354376
*/
@@ -442,6 +464,33 @@ function AttachmentPicker({
442464
[handleImageProcessingError, shouldValidateImage, showGeneralAlert, showImageCorruptionAlert],
443465
);
444466

467+
/**
468+
* Opens the attachment modal, or directly launches the document picker when shouldSkipAttachmentTypeModal is true.
469+
*/
470+
const open = (onPickedHandler: (files: FileObject[]) => void, onCanceledHandler: () => void = () => {}, onClosedHandler: () => void = () => {}) => {
471+
completeAttachmentSelection.current = onPickedHandler;
472+
onCanceled.current = onCanceledHandler;
473+
onClosed.current = onClosedHandler;
474+
475+
if (shouldSkipAttachmentTypeModal) {
476+
onOpenPicker?.();
477+
showDocumentPicker()
478+
.catch((error: Error) => {
479+
if (JSON.stringify(error).includes('OPERATION_CANCELED')) {
480+
return;
481+
}
482+
showGeneralAlert(error.message);
483+
throw error;
484+
})
485+
.then((result) => pickAttachment(result))
486+
.catch(console.error)
487+
.finally(() => onClosedHandler());
488+
return;
489+
}
490+
491+
setIsVisible(true);
492+
};
493+
445494
/**
446495
* Setup native attachment selection to start after this popover closes
447496
*

src/components/AttachmentPicker/types.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@ type AttachmentPickerProps = {
6060

6161
/** A callback that will be called when the picker is opened. */
6262
onOpenPicker?: () => void;
63+
64+
/** When true, skip the Camera/Gallery/Document modal and open the document picker directly (native only). */
65+
shouldSkipAttachmentTypeModal?: boolean;
6366
};
6467

6568
export default AttachmentPickerProps;

src/languages/de.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6841,6 +6841,8 @@ Fordern Sie Spesendetails wie Belege und Beschreibungen an, legen Sie Limits und
68416841
customRules: {
68426842
title: 'Spesenrichtlinie',
68436843
cardSubtitle: 'Hier ist die Spesenrichtlinie deines Teams hinterlegt, damit alle denselben Stand haben, was abgedeckt ist.',
6844+
policyDocument: 'Richtliniendokument',
6845+
policyText: 'Richtlinientext',
68446846
},
68456847
spendRules: {
68466848
title: 'Ausgaben',

src/languages/en.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6857,6 +6857,8 @@ const translations = {
68576857
customRules: {
68586858
title: 'Expense policy',
68596859
cardSubtitle: "Here's where your team's expense policy lives, so everyone's on the same page about what's covered.",
6860+
policyDocument: 'Policy document',
6861+
policyText: 'Policy text',
68606862
},
68616863
spendRules: {
68626864
title: 'Spend',

src/languages/es.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6744,6 +6744,8 @@ ${amount} para ${merchant} - ${date}`,
67446744
customRules: {
67456745
title: 'Reglas personalizadas',
67466746
cardSubtitle: 'Aquí es donde se definen las reglas de tu equipo, para que todos sepan lo que esta cubierto.',
6747+
policyDocument: 'Documento de política',
6748+
policyText: 'Texto de política',
67476749
},
67486750
spendRules: {
67496751
title: 'Gastos',

src/languages/fr.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6863,6 +6863,8 @@ Rendez obligatoires des informations de dépense comme les reçus et les descrip
68636863
customRules: {
68646864
title: 'Politique de dépenses',
68656865
cardSubtitle: 'C’est ici que se trouve la politique de dépenses de votre équipe, pour que tout le monde soit d’accord sur ce qui est couvert.',
6866+
policyDocument: 'Document de politique',
6867+
policyText: 'Texte de politique',
68666868
},
68676869
spendRules: {
68686870
title: 'Dépenser',

src/languages/it.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6826,6 +6826,8 @@ Richiedi dettagli sulle spese come ricevute e descrizioni, imposta limiti e valo
68266826
customRules: {
68276827
title: 'Politica di spesa',
68286828
cardSubtitle: 'Qui trovi il regolamento spese del tuo team, così tutti sono allineati su cosa è coperto.',
6829+
policyDocument: 'Documento di politica',
6830+
policyText: 'Testo della politica',
68296831
},
68306832
spendRules: {
68316833
title: 'Spesa',

0 commit comments

Comments
 (0)