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
2 changes: 2 additions & 0 deletions src/components/Form/FormWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ function FormWrapper({
submitFlexEnabled = true,
enabledWhenOffline,
buttonVariant,
isSubmitButtonNeutral = false,
formID,
shouldUseScrollView = true,
scrollContextEnabled = false,
Expand Down Expand Up @@ -217,6 +218,7 @@ function FormWrapper({
]}
enabledWhenOffline={enabledWhenOffline}
buttonVariant={buttonVariant}
isButtonNeutral={isSubmitButtonNeutral}
disablePressOnEnter={disablePressOnEnter}
enterKeyEventListenerPriority={enterKeyEventListenerPriority}
shouldRenderFooterAboveSubmit={shouldRenderFooterAboveSubmit}
Expand Down
3 changes: 3 additions & 0 deletions src/components/Form/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,9 @@ type FormProps<TFormID extends OnyxFormKey = OnyxFormKey> = ForwardedFSClassProp
/** The visual variant of the submit button, which controls its color scheme */
buttonVariant?: ButtonVariant;

/** Whether the submit button has the neutral color in place of `buttonVariant`, so that a button in `footerContent` can be the suggested action */
isSubmitButtonNeutral?: boolean;

/** Should fix the errors alert be displayed when there is an error in the form */
shouldHideFixErrorsAlert?: boolean;

Expand Down
9 changes: 7 additions & 2 deletions src/components/FormAlertWithSubmitButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ type FormAlertWithSubmitButtonProps = WithSentryLabel & {
/** The visual variant of the submit button, which controls its color scheme */
buttonVariant?: ButtonVariant;

/** Whether the submit button has the neutral color in place of `buttonVariant` */
isButtonNeutral?: boolean;

/** Custom content to display in the footer after submit button */
footerContent?: React.ReactNode;

Expand Down Expand Up @@ -98,6 +101,7 @@ function FormAlertWithSubmitButton({
enabledWhenOffline = false,
disablePressOnEnter = false,
buttonVariant = CONST.BUTTON_VARIANT.SUCCESS,
isButtonNeutral = false,
footerContent,
buttonRef,
buttonStyles,
Expand All @@ -116,6 +120,7 @@ function FormAlertWithSubmitButton({
}: FormAlertWithSubmitButtonProps) {
const styles = useThemeStyles();
const style = [!shouldRenderFooterAboveSubmit && footerContent && addButtonBottomPadding ? styles.mb3 : undefined, buttonStyles];
const variant = isButtonNeutral ? undefined : buttonVariant;

const {isLoading, startWithLoading} = usePressLoading({isLoading: isOnyxLoading});

Expand Down Expand Up @@ -147,7 +152,7 @@ function FormAlertWithSubmitButton({
{shouldRenderFooterAboveSubmit && footerContent}
{isOffline && !enabledWhenOffline ? (
<Button
variant={buttonVariant}
variant={variant}
blendOpacity={blendButtonOpacity}
isDisabled
size={CONST.BUTTON_SIZE.LARGE}
Expand All @@ -160,7 +165,7 @@ function FormAlertWithSubmitButton({
) : (
<Button
ref={buttonRef}
variant={buttonVariant}
variant={variant}
blendOpacity={blendButtonOpacity}
size={CONST.BUTTON_SIZE.LARGE}
style={style}
Expand Down
5 changes: 4 additions & 1 deletion src/languages/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8952,7 +8952,10 @@ Fügen Sie weitere Ausgabelimits hinzu, um den Cashflow Ihres Unternehmens zu sc
missingData: 'Expensify hat diese Information nicht.',
retroactive: 'Regeln gelten nur für neue Aktivitäten.',
historyLimit: 'Regeln können nicht so weit zurückblicken.',
tryThisInstead: 'Versuchen Sie es stattdessen damit',
useSuggestion: 'Vorschlag verwenden',
explanationWithSuggestion: (explanation: string) => `${explanation} Probieren Sie stattdessen den Vorschlag unten aus:`,
quotedSuggestion: (suggestedPrompt: string) => `„${suggestedPrompt}“`,
createAnyway: 'Trotzdem erstellen',
},
},
expenseDefaultsTable: {
Expand Down
5 changes: 4 additions & 1 deletion src/languages/el.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9041,7 +9041,10 @@ ${reportName}`,
missingData: 'Η Expensify δεν διαθέτει αυτές τις πληροφορίες.',
retroactive: 'Οι κανόνες ισχύουν μόνο για νέα δραστηριότητα.',
historyLimit: 'Οι κανόνες δεν μπορούν να ανατρέξουν τόσο πίσω.',
tryThisInstead: 'Δοκιμάστε αυτό αντί για εκείνο',
useSuggestion: 'Χρήση πρότασης',
explanationWithSuggestion: (explanation: string) => `${explanation} Δοκιμάστε αντ’ αυτού την παρακάτω πρόταση:`,
quotedSuggestion: (suggestedPrompt: string) => `«${suggestedPrompt}»`,
createAnyway: 'Δημιουργία ούτως ή άλλως',
},
},
expenseDefaultsTable: {
Expand Down
5 changes: 4 additions & 1 deletion src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9316,7 +9316,10 @@ const translations = {
missingData: "Expensify doesn't have that information.",
retroactive: 'Rules only apply to new activity.',
historyLimit: "Rules can't look back that far.",
tryThisInstead: 'Try this instead',
useSuggestion: 'Use suggestion',
explanationWithSuggestion: (explanation: string) => `${explanation} Try the suggestion below instead:`,
quotedSuggestion: (suggestedPrompt: string) => `"${suggestedPrompt}"`,
createAnyway: 'Create anyway',
},
},
expenseDefaultsTable: {
Expand Down
5 changes: 4 additions & 1 deletion src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8960,7 +8960,10 @@ ${reportName}`,
missingData: 'Expensify no tiene esa información.',
retroactive: 'Las reglas solo se aplican a la actividad nueva.',
historyLimit: 'Las reglas no pueden mirar tan atrás.',
tryThisInstead: 'Prueba esto en su lugar',
useSuggestion: 'Usar sugerencia',
explanationWithSuggestion: (explanation: string) => `${explanation} Prueba en su lugar la sugerencia de abajo:`,
quotedSuggestion: (suggestedPrompt: string) => `"${suggestedPrompt}"`,
createAnyway: 'Crear de todos modos',
},
},
expenseDefaultsTable: {
Expand Down
5 changes: 4 additions & 1 deletion src/languages/fr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8975,7 +8975,10 @@ Ajoutez davantage de règles de dépenses pour protéger la trésorerie de l’e
missingData: 'Expensify n’a pas ces informations.',
retroactive: 'Les règles ne s’appliquent qu’aux nouvelles activités.',
historyLimit: 'Les règles ne peuvent pas remonter aussi loin dans le temps.',
tryThisInstead: 'Essayez plutôt ceci',
useSuggestion: 'Utiliser la suggestion',
explanationWithSuggestion: (explanation: string) => `${explanation} Essayez plutôt la suggestion ci-dessous :`,
quotedSuggestion: (suggestedPrompt: string) => `« ${suggestedPrompt} »`,
createAnyway: 'Créer quand même',
},
},
expenseDefaultsTable: {
Expand Down
5 changes: 4 additions & 1 deletion src/languages/it.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8906,7 +8906,10 @@ Aggiungi altre regole di spesa per proteggere il flusso di cassa aziendale.`,
missingData: 'Expensify non ha queste informazioni.',
retroactive: 'Le regole si applicano solo alla nuova attività.',
historyLimit: 'Le regole non possono risalire così indietro.',
tryThisInstead: 'Prova questo invece',
useSuggestion: 'Usa il suggerimento',
explanationWithSuggestion: (explanation: string) => `${explanation} Prova invece il suggerimento qui sotto:`,
quotedSuggestion: (suggestedPrompt: string) => `"${suggestedPrompt}"`,
createAnyway: 'Crea comunque',
},
},
expenseDefaultsTable: {
Expand Down
5 changes: 4 additions & 1 deletion src/languages/ja.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8789,7 +8789,10 @@ ${reportName}`,
missingData: 'Expensify はその情報を持っていません。',
retroactive: 'ルールは新しいアクティビティにのみ適用されます。',
historyLimit: 'ルールはそれほど前までさかのぼることができません。',
tryThisInstead: '代わりにこちらをお試しください',
useSuggestion: '提案を使用',
explanationWithSuggestion: (explanation: string) => `${explanation}代わりに以下の提案をお試しください:`,
quotedSuggestion: (suggestedPrompt: string) => `「${suggestedPrompt}」`,
createAnyway: 'このまま作成',
},
},
expenseDefaultsTable: {
Expand Down
5 changes: 4 additions & 1 deletion src/languages/nl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8878,7 +8878,10 @@ er bestedingsregels toe om de kasstroom van het bedrijf te beschermen.`,
missingData: 'Expensify heeft die informatie niet.',
retroactive: 'Regels zijn alleen van toepassing op nieuwe activiteit.',
historyLimit: 'Regels kunnen niet zo ver terugkijken.',
tryThisInstead: 'Probeer dit in plaats daarvan',
useSuggestion: 'Suggestie gebruiken',
explanationWithSuggestion: (explanation: string) => `${explanation} Probeer in plaats daarvan de suggestie hieronder:`,
quotedSuggestion: (suggestedPrompt: string) => `‘${suggestedPrompt}’`,
createAnyway: 'Toch maken',
},
},
expenseDefaultsTable: {
Expand Down
5 changes: 4 additions & 1 deletion src/languages/pl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8905,7 +8905,10 @@ Dodaj więcej zasad wydatków, żeby chronić płynność finansową firmy.`,
missingData: 'Expensify nie ma tych informacji.',
retroactive: 'Zasady mają zastosowanie tylko do nowej aktywności.',
historyLimit: 'Reguły nie mogą sięgać tak daleko wstecz.',
tryThisInstead: 'Spróbuj tego zamiast tego',
useSuggestion: 'Użyj sugestii',
explanationWithSuggestion: (explanation: string) => `${explanation} Zamiast tego wypróbuj poniższą sugestię:`,
quotedSuggestion: (suggestedPrompt: string) => `„${suggestedPrompt}”`,
createAnyway: 'Utwórz mimo to',
},
},
expenseDefaultsTable: {
Expand Down
5 changes: 4 additions & 1 deletion src/languages/pt-BR.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8884,7 +8884,10 @@ Adicione mais regras de gasto para proteger o fluxo de caixa da empresa.`,
missingData: 'A Expensify não tem essa informação.',
retroactive: 'As regras se aplicam apenas à nova atividade.',
historyLimit: 'As regras não conseguem olhar tão para trás.',
tryThisInstead: 'Tente isto em vez disso',
useSuggestion: 'Usar sugestão',
explanationWithSuggestion: (explanation: string) => `${explanation} Experimente a sugestão abaixo:`,
quotedSuggestion: (suggestedPrompt: string) => `"${suggestedPrompt}"`,
createAnyway: 'Criar mesmo assim',
},
},
expenseDefaultsTable: {
Expand Down
5 changes: 4 additions & 1 deletion src/languages/zh-hans.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8569,7 +8569,10 @@ ${reportName}`,
missingData: 'Expensify 没有该信息。',
retroactive: '规则仅适用于新活动。',
historyLimit: '规则无法追溯到那么久以前。',
tryThisInstead: '试试这个',
useSuggestion: '使用建议',
explanationWithSuggestion: (explanation: string) => `${explanation}请改为尝试下面的建议:`,
quotedSuggestion: (suggestedPrompt: string) => `“${suggestedPrompt}”`,
createAnyway: '仍然创建',
},
},
expenseDefaultsTable: {
Expand Down
55 changes: 36 additions & 19 deletions src/pages/workspace/rules/DescribeRuleForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,8 @@ function DescribeRuleForm({
onFinish,
}: DescribeRuleFormProps) {
const {translate} = useLocalize();
const styles = useThemeStyles();
const {isOffline} = useNetwork();
const styles = useThemeStyles();
const formRef = useRef<FormRef>(null);
const inputRef = useRef<AnimatedTextInputRef>(null);
const inputTopOffsetRef = useRef(0);
Expand All @@ -165,6 +165,11 @@ function DescribeRuleForm({
const generatedAgentRulePrompt = generatedRule?.agentRulePrompt;
const refusal = generatedRuleState === CONST.GENERATED_RULE.STATE.IMPOSSIBLE && !!generatedRuleID && generatedRuleID === submittedGenerationID ? generatedRule : undefined;
const refusalSuggestedPrompt = refusal?.suggestedPrompt;
const canCreateAnyway = !!refusalSuggestedPrompt && !!onAgentRuleFallback;
// An answer without an explanation still says why, with the general text for its reason
const refusalExplanation =
refusal && (refusal.explanation ? refusal.explanation : translate(IMPOSSIBLE_TITLE_BY_REASON[refusal.reason ?? ''] ?? 'workspace.rules.newRule.impossible.noCapability'));
const refusalDetail = refusalExplanation && refusalSuggestedPrompt ? translate('workspace.rules.newRule.impossible.explanationWithSuggestion', refusalExplanation) : refusalExplanation;
const inputLabel = translate('workspace.rules.newRule.describeRuleInputLabel');

const finish = () => {
Expand Down Expand Up @@ -279,6 +284,13 @@ function DescribeRuleForm({
setNewRulePromptEmptyError();
return;
}
// The admin keeps their own description over the suggestion. Therefore it goes to the agent rule page and not back to Concierge.
if (refusal && refusalSuggestedPrompt && onAgentRuleFallback) {
Log.info('[GenerateRule] Created an agent rule after a refusal', false, {policyID, generationID: refusal.generationID, reason: refusal.reason});
onAgentRuleFallback(prompt, refusal.generationID, refusal.state);
finish();
return;
}
describeRule(prompt);
};

Expand All @@ -298,12 +310,32 @@ function DescribeRuleForm({
formID={ONYXKEYS.FORMS.NEW_RULE_PROMPT_FORM}
validate={validate}
onSubmit={submit}
submitButtonText={submitButtonText}
submitButtonText={canCreateAnyway ? translate('workspace.rules.newRule.impossible.createAnyway') : submitButtonText}
style={style}
addBottomSafeAreaPadding={addBottomSafeAreaPadding}
shouldUseScrollView={shouldUseScrollView}
submitFlexEnabled={submitFlexEnabled}
shouldHideFixErrorsAlert
isSubmitButtonNeutral={!!refusalSuggestedPrompt}
// The buttons share a row and wrap to two rows when a translated label does not fit beside the other one
submitButtonAndFooterContainerStyles={refusalSuggestedPrompt ? [styles.flexRow, styles.flexWrap, styles.gap2] : undefined}
// The form puts a bottom margin on the submit button when the footer follows it. Side by side, the buttons must line up.
submitButtonInnerStyles={refusalSuggestedPrompt ? [styles.flexGrow1, styles.flexBasisAuto, styles.mb0] : undefined}
footerContent={
!!refusalSuggestedPrompt && (
<Button
variant={CONST.BUTTON_VARIANT.SUCCESS}
size={CONST.BUTTON_SIZE.LARGE}
onPress={() => describeSuggestedRule(refusalSuggestedPrompt)}
// Like the form's submit button, this button sends GenerateRule, which needs a connection
isDisabled={isOffline}
style={[styles.flexGrow1, styles.flexBasisAuto]}
sentryLabel={CONST.SENTRY_LABEL.WORKSPACE.RULES.DESCRIBE_RULE_TRY_SUGGESTION}
>
<Button.Text>{translate('workspace.rules.newRule.impossible.useSuggestion')}</Button.Text>
</Button>
)
}
// An empty description sends no request, so nothing would stop a loading state that starts on the press.
shouldShowLoadingImmediatelyOnPress={!canSkipDescription}
keyboardSubmitBehavior={CONST.KEYBOARD_SUBMIT_BEHAVIOR.SUBMIT_ONLY}
Expand Down Expand Up @@ -338,23 +370,8 @@ function DescribeRuleForm({
</View>
{!!refusal && (
<View style={styles.mt4}>
<Text style={styles.textStrong}>{translate(IMPOSSIBLE_TITLE_BY_REASON[refusal.reason ?? ''] ?? 'workspace.rules.newRule.impossible.noCapability')}</Text>
{!!refusal.explanation && <Text style={[styles.textSupporting, styles.mt1]}>{refusal.explanation}</Text>}
{!!refusalSuggestedPrompt && (
<>
<Text style={styles.mt3}>{refusalSuggestedPrompt}</Text>
<Button
size={CONST.BUTTON_SIZE.SMALL}
onPress={() => describeSuggestedRule(refusalSuggestedPrompt)}
// Like the form's submit button, this button sends GenerateRule, which needs a connection
isDisabled={isOffline}
style={[styles.mt2, styles.alignSelfStart]}
sentryLabel={CONST.SENTRY_LABEL.WORKSPACE.RULES.DESCRIBE_RULE_TRY_SUGGESTION}
>
<Button.Text>{translate('workspace.rules.newRule.impossible.tryThisInstead')}</Button.Text>
</Button>
</>
)}
{!!refusalDetail && <Text style={styles.textSupporting}>{refusalDetail}</Text>}
{!!refusalSuggestedPrompt && <Text style={styles.mt3}>{translate('workspace.rules.newRule.impossible.quotedSuggestion', refusalSuggestedPrompt)}</Text>}
</View>
)}
</FormProvider>
Expand Down
Loading
Loading