Skip to content

Commit

Permalink
Merge branch 'main' into @chrispader/theme-styles-consistent-naming
Browse files Browse the repository at this point in the history
  • Loading branch information
chrispader committed Dec 6, 2023
2 parents 17a924f + a42894e commit 43c3bf3
Show file tree
Hide file tree
Showing 32 changed files with 583 additions and 241 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
title: Your Expensify Partner Manager
title: Expensify Partner Support
description: Understanding support for our partners
---

Expand All @@ -10,23 +10,47 @@ Our well-rounded support methodology is designed to provide comprehensive assist

## 1. ExpensifyApproved! University
**Purpose:** Equip your team with a comprehensive understanding of Expensify.

**Benefits:**
- Foundation-level knowledge about the platform.
- 3 CPE credits upon successful completion (US-only).
- Unlock exclusive partner perks, including tickets to ExpensiCon!
- Visit university.Expensify.com to access our comprehensive training program.

## 2. Partner Manager
**Role:** A designated liaison for your firm.
**Role:**
A Partner Manager is a dedicated point of contact for your firm Partner Managers support our accounting partners by providing recommendations for client’s accounts, assisting with firm-wide training, and ensuring partners receive the full benefits of our partnership program. They will actively monitor open technical issues and be proactive with recommendations to increase efficiency.


**Key Responsibilities:**
- Handle any escalations promptly.
- Organize firm-wide training sessions.
- Assist with strategic planning and the introduction of new features.
- Once you've completed the ExpensifyApproved! University, log in to your Expensify account. Click on the "Support" option to connect with your dedicated Partner Manager.

**How do I know if I have a Partner Manager?**

For your firm to be assigned a Partner Manager, you must complete the ExpensifyApproved! University training course. Every external accountant or bookkeeper who completes the training is automatically enrolled in our program and receives all the benefits, including access to the Partner Manager. So everyone at your firm must complete the training to receive the maximum benefit.

You can check to see if you’ve completed the course and enrolled in the ExpensifyApproved! Accountants program simply by logging into your Expensify account. In the bottom left-hand corner of the website, you will see the ExpensifyApproved! logo.

**How do I contact my Partner Manager?**
1. Signing in to new.expensify.com and searching for your Partner Manager
2. Replying to or clicking the chat link on any email you get from your Partner Manager

**How do I know if my Partner Manager is online?**

You will be able to see if they are online via their status in new.expensify.com, which will either say “online” or have their working hours.

**Can I get on a call with my Partner Manager?**

Of course! You can ask your Partner Manager to schedule a call whenever you think one might be helpful. Partner Managers can discuss client onboarding strategies, firm-wide training, and client setups.

We recommend continuing to work with Concierge for general support questions, as this team is always online and available to help immediately.

## 3. Client Setup Specialist
**Purpose:** Ensure smooth onboarding for every client you refer.

**Duties:**
- Comprehensive assistance with setting up Expensify.
- Help with configuring accounting integrations.
Expand All @@ -35,13 +59,15 @@ Our well-rounded support methodology is designed to provide comprehensive assist

## 4. Client Account Manager
**Role:** Dedicated support for ongoing client needs.

**Responsibilities:**
- Address day-to-day product inquiries.
- Assist clients in navigating and optimizing their use of Expensify.
- After logging into Expensify, click on the "Support" option in the left-hand navigation pane. This will connect you directly to your assigned Account Manager.

## 5. Concierge chat support
**Availability:** Real-time support for any urgent inquiries.

**Features:**
- Immediate assistance with an average response time of under two minutes.
- Available to both accountants and clients for all product-related questions.
Expand Down
45 changes: 45 additions & 0 deletions docs/articles/expensify-classic/get-paid-back/Per-Diem-Expenses.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
---
title: Per-Diem-Expenses
description: How to create Per Diem expenses on mobile and web.
---
# Overview

What are Per Diems? Per diems, short for "per diem allowance" or "daily allowance," are fixed daily payments provided by your employer to cover expenses incurred during business or work-related travel. These allowances simplify expense tracking and reimbursement for meals, lodging, and incidental expenses during a trip. Per Diems can be masterfully tracked in Expensify!

## How to create per diem expenses

To add per diem expenses, you need three pieces of information:
1. Where did you go? - Specify your travel destination.
2. How long were you away? - Define the period you're claiming for.
3. Which rate did you use? - Select the appropriate per diem rate (this is set by your employer).

### Step 1: On either the web or mobile app, click New Expense and choose Per Diem

### Step 2: Select your travel destination from the Destination dropdown
If your trip involves multiple stops, create a separate Per Diem expense for each destination. Remember, the times are relative to your home city.

### Step 3: Select your Start date, End date, Start time, and End time
Expensify will automatically calculate the duration of your trip. We'll show you a breakdown of your days: the time between departure and midnight on the first day, the total days in between, and the time between midnight and your return time on the last day.

### Step 4: Select a Subrate based on the trip duration from the dropdown list
You can include meal deductions or overnight lodging costs if your jurisdiction permits.

### Step 5: Enter any other required coding and click “Save”

### Step 6: Submit for Approval
Finally, submit your Per Diem expense for approval, and you'll be on your way to getting reimbursed!

# FAQ

## Can I edit my per diem expenses?
Per Diems cannot be amended. To make changes, delete the expense and recreate it as needed.

## What if my admin requires daily per diems?
No problem! Create a separate Per Diem expense for each day of your trip.

## I have questions about the amount I'm due
Reach out to your internal Admin team, as they've configured the rates in your policy to meet specific requirements.

## Can I add start and end times to per diems?
Unfortunately, you cannot add start and end times to Per Diems in Expensify.
By following these steps, you can efficiently create and manage your Per Diem expenses in Expensify, making the process of tracking and getting reimbursed hassle-free.
5 changes: 0 additions & 5 deletions docs/articles/expensify-classic/get-paid-back/Per-Diem.md

This file was deleted.

1 change: 1 addition & 0 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,7 @@ const CONST = {
MAX_REPORT_PREVIEW_RECEIPTS: 3,
},
REPORT: {
MAX_COUNT_BEFORE_FOCUS_UPDATE: 30,
MAXIMUM_PARTICIPANTS: 8,
SPLIT_REPORTID: '-2',
ACTIONS: {
Expand Down
10 changes: 10 additions & 0 deletions src/Expensify.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import _ from 'underscore';
import ConfirmModal from './components/ConfirmModal';
import DeeplinkWrapper from './components/DeeplinkWrapper';
import EmojiPicker from './components/EmojiPicker/EmojiPicker';
import FocusModeNotification from './components/FocusModeNotification';
import GrowlNotification from './components/GrowlNotification';
import AppleAuthWrapper from './components/SignInButtons/AppleAuthWrapper';
import SplashScreenHider from './components/SplashScreenHider';
Expand Down Expand Up @@ -76,6 +77,9 @@ const propTypes = {
/** Whether the app is waiting for the server's response to determine if a room is public */
isCheckingPublicRoom: PropTypes.bool,

/** Whether we should display the notification alerting the user that focus mode has been auto-enabled */
focusModeNotification: PropTypes.bool,

...withLocalizePropTypes,
};

Expand All @@ -88,6 +92,7 @@ const defaultProps = {
isSidebarLoaded: false,
screenShareRequest: null,
isCheckingPublicRoom: true,
focusModeNotification: false,
};

const SplashScreenHiddenContext = React.createContext({});
Expand Down Expand Up @@ -221,6 +226,7 @@ function Expensify(props) {
isVisible
/>
) : null}
{props.focusModeNotification ? <FocusModeNotification /> : null}
</>
)}

Expand Down Expand Up @@ -261,6 +267,10 @@ export default compose(
screenShareRequest: {
key: ONYXKEYS.SCREEN_SHARE_REQUEST,
},
focusModeNotification: {
key: ONYXKEYS.FOCUS_MODE_NOTIFICATION,
initWithStoredValues: false,
},
}),
)(Expensify);

Expand Down
8 changes: 8 additions & 0 deletions src/ONYXKEYS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,12 @@ const ONYXKEYS = {
/** The user's cash card and imported cards (including the Expensify Card) */
CARD_LIST: 'cardList',

/** Whether the user has tried focus mode yet */
NVP_TRY_FOCUS_MODE: 'tryFocusMode',

/** Boolean flag used to display the focus mode notification */
FOCUS_MODE_NOTIFICATION: 'focusModeNotification',

/** Stores information about the user's saved statements */
WALLET_STATEMENT: 'walletStatement',

Expand Down Expand Up @@ -383,6 +389,8 @@ type OnyxValues = {
[ONYXKEYS.NVP_PRIORITY_MODE]: ValueOf<typeof CONST.PRIORITY_MODE>;
[ONYXKEYS.NVP_BLOCKED_FROM_CONCIERGE]: OnyxTypes.BlockedFromConcierge;
[ONYXKEYS.NVP_PRIVATE_PUSH_NOTIFICATION_ID]: string;
[ONYXKEYS.NVP_TRY_FOCUS_MODE]: boolean;
[ONYXKEYS.FOCUS_MODE_NOTIFICATION]: boolean;
[ONYXKEYS.NVP_LAST_PAYMENT_METHOD]: Record<string, string>;
[ONYXKEYS.NVP_RECENT_WAYPOINTS]: OnyxTypes.RecentWaypoint[];
[ONYXKEYS.PUSH_NOTIFICATIONS_ENABLED]: boolean;
Expand Down
4 changes: 2 additions & 2 deletions src/components/Attachments/AttachmentCarousel/CarouselItem.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ const propTypes = {
/** Additional information about the attachment file */
file: PropTypes.shape({
/** File name of the attachment */
name: PropTypes.string,
}),
name: PropTypes.string.isRequired,
}).isRequired,

/** Whether the attachment has been flagged */
hasBeenFlagged: PropTypes.bool,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {Parser as HtmlParser} from 'htmlparser2';
import _ from 'underscore';
import * as FileUtils from '@libs/fileDownload/FileUtils';
import * as ReportActionsUtils from '@libs/ReportActionsUtils';
import tryResolveUrlFromApiRoot from '@libs/tryResolveUrlFromApiRoot';
import CONST from '@src/CONST';
Expand All @@ -21,14 +22,16 @@ function extractAttachmentsFromReport(parentReportAction, reportActions) {
}

const expensifySource = attribs[CONST.ATTACHMENT_SOURCE_ATTRIBUTE];
const source = tryResolveUrlFromApiRoot(expensifySource || attribs.src);
const fileName = attribs[CONST.ATTACHMENT_ORIGINAL_FILENAME_ATTRIBUTE] || FileUtils.getFileName(`${source}`);

// By iterating actions in chronological order and prepending each attachment
// we ensure correct order of attachments even across actions with multiple attachments.
attachments.unshift({
source,
reportActionID: attribs['data-id'],
source: tryResolveUrlFromApiRoot(expensifySource || attribs.src),
isAuthTokenRequired: Boolean(expensifySource),
file: {name: attribs[CONST.ATTACHMENT_ORIGINAL_FILENAME_ATTRIBUTE]},
file: {name: fileName},
hasBeenFlagged: attribs['data-flagged'] === 'true',
});
},
Expand Down
4 changes: 2 additions & 2 deletions src/components/Attachments/propTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import PropTypes from 'prop-types';

const attachmentSourcePropType = PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.number]);
const attachmentFilePropType = PropTypes.shape({
name: PropTypes.string,
name: PropTypes.string.isRequired,
});

const attachmentPropType = PropTypes.shape({
Expand All @@ -13,7 +13,7 @@ const attachmentPropType = PropTypes.shape({
source: attachmentSourcePropType.isRequired,

/** File object can be an instance of File or Object */
file: attachmentFilePropType,
file: attachmentFilePropType.isRequired,
});

const attachmentsPropType = PropTypes.arrayOf(attachmentPropType);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,75 +1,63 @@
import PropTypes from 'prop-types';
import React from 'react';
import {View} from 'react-native';
import useThemeStyles from '@styles/useThemeStyles';
import Button from './Button';
import FixedFooter from './FixedFooter';
import Lottie from './Lottie';
import LottieAnimations from './LottieAnimations';
import DotLottieAnimation from './LottieAnimations/types';
import Text from './Text';

const propTypes = {
type ConfirmationPageProps = {
/** The asset to render */
// eslint-disable-next-line react/forbid-prop-types
animation: PropTypes.object,
animation?: DotLottieAnimation;

/** Heading of the confirmation page */
heading: PropTypes.string,
heading: string;

/** Description of the confirmation page */
description: PropTypes.string,
description: string;

/** The text for the button label */
buttonText: PropTypes.string,
buttonText?: string;

/** A function that is called when the button is clicked on */
onButtonPress: PropTypes.func,
onButtonPress?: () => void;

/** Whether we should show a confirmation button */
shouldShowButton: PropTypes.bool,
shouldShowButton?: boolean;
};

const defaultProps = {
animation: LottieAnimations.Fireworks,
heading: '',
description: '',
buttonText: '',
onButtonPress: () => {},
shouldShowButton: false,
};

function ConfirmationPage(props) {
function ConfirmationPage({animation = LottieAnimations.Fireworks, heading, description, buttonText = '', onButtonPress = () => {}, shouldShowButton = false}: ConfirmationPageProps) {
const styles = useThemeStyles();

return (
<>
<View style={[styles.screenCenteredContainer, styles.alignItemsCenter]}>
<Lottie
source={props.animation}
source={animation}
autoPlay
loop
style={styles.confirmationAnimation}
webStyle={styles.confirmationAnimationWeb}
/>
<Text style={[styles.textHeadline, styles.textAlignCenter, styles.mv2]}>{props.heading}</Text>
<Text style={styles.textAlignCenter}>{props.description}</Text>
<Text style={[styles.textHeadline, styles.textAlignCenter, styles.mv2]}>{heading}</Text>
<Text style={styles.textAlignCenter}>{description}</Text>
</View>
{props.shouldShowButton && (
{shouldShowButton && (
<FixedFooter>
<Button
success
text={props.buttonText}
text={buttonText}
style={styles.mt6}
pressOnEnter
onPress={props.onButtonPress}
onPress={onButtonPress}
/>
</FixedFooter>
)}
</>
);
}

ConfirmationPage.propTypes = propTypes;
ConfirmationPage.defaultProps = defaultProps;
ConfirmationPage.displayName = 'ConfirmationPage';

export default ConfirmationPage;
2 changes: 1 addition & 1 deletion src/components/FixedFooter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ type FixedFooterProps = {
style?: StyleProp<ViewStyle>;
};

function FixedFooter({style = [], children}: FixedFooterProps) {
function FixedFooter({style, children}: FixedFooterProps) {
const styles = useThemeStyles();
return <View style={[styles.ph5, styles.pb5, styles.flexShrink0, style]}>{children}</View>;
}
Expand Down
Loading

0 comments on commit 43c3bf3

Please sign in to comment.