✨ (dashboards) release as first party feature (#3856)
@@ -22,8 +22,9 @@ export class ReportsPage {
|
||||
|
||||
async goToCustomReportPage() {
|
||||
await this.pageContent
|
||||
.getByRole('button', { name: 'Create new custom report' })
|
||||
.getByRole('button', { name: 'Add new widget' })
|
||||
.click();
|
||||
await this.page.getByRole('button', { name: 'New custom report' }).click();
|
||||
return new CustomReportPage(this.page);
|
||||
}
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 70 KiB After Width: | Height: | Size: 71 KiB |
|
Before Width: | Height: | Size: 70 KiB After Width: | Height: | Size: 71 KiB |
|
Before Width: | Height: | Size: 69 KiB After Width: | Height: | Size: 70 KiB |
|
Before Width: | Height: | Size: 73 KiB After Width: | Height: | Size: 74 KiB |
|
Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 73 KiB |
|
Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 73 KiB |
|
Before Width: | Height: | Size: 79 KiB After Width: | Height: | Size: 80 KiB |
|
Before Width: | Height: | Size: 79 KiB After Width: | Height: | Size: 81 KiB |
|
Before Width: | Height: | Size: 77 KiB After Width: | Height: | Size: 78 KiB |
@@ -7,7 +7,6 @@ import {
|
||||
type TimeFrame,
|
||||
} from 'loot-core/types/models';
|
||||
|
||||
import { useFeatureFlag } from '../../hooks/useFeatureFlag';
|
||||
import { Button } from '../common/Button2';
|
||||
import { Select } from '../common/Select';
|
||||
import { SpaceBetween } from '../common/SpaceBetween';
|
||||
@@ -62,7 +61,6 @@ export function Header({
|
||||
children,
|
||||
}: HeaderProps) {
|
||||
const { t } = useTranslation();
|
||||
const isDashboardsFeatureEnabled = useFeatureFlag('dashboards');
|
||||
const { isNarrowWidth } = useResponsive();
|
||||
|
||||
return (
|
||||
@@ -80,7 +78,7 @@ export function Header({
|
||||
}}
|
||||
>
|
||||
<SpaceBetween gap={isNarrowWidth ? 5 : undefined}>
|
||||
{isDashboardsFeatureEnabled && mode && (
|
||||
{mode && (
|
||||
<Button
|
||||
variant={mode === 'static' ? 'normal' : 'primary'}
|
||||
onPress={() => {
|
||||
|
||||
@@ -20,7 +20,6 @@ import {
|
||||
} from 'loot-core/src/types/models';
|
||||
|
||||
import { useAccounts } from '../../hooks/useAccounts';
|
||||
import { useFeatureFlag } from '../../hooks/useFeatureFlag';
|
||||
import { useNavigate } from '../../hooks/useNavigate';
|
||||
import { breakpoints } from '../../tokens';
|
||||
import { Button } from '../common/Button2';
|
||||
@@ -79,10 +78,7 @@ export function Overview() {
|
||||
const location = useLocation();
|
||||
sessionStorage.setItem('url', location.pathname);
|
||||
|
||||
const isDashboardsFeatureEnabled = useFeatureFlag('dashboards');
|
||||
|
||||
const baseLayout = widgets
|
||||
.map(widget => ({
|
||||
const baseLayout = widgets.map(widget => ({
|
||||
i: widget.id,
|
||||
w: widget.width,
|
||||
h: widget.height,
|
||||
@@ -91,16 +87,7 @@ export function Overview() {
|
||||
minH:
|
||||
isCustomReportWidget(widget) || widget.type === 'markdown-card' ? 1 : 2,
|
||||
...widget,
|
||||
}))
|
||||
.filter(item => {
|
||||
if (isDashboardsFeatureEnabled) {
|
||||
return true;
|
||||
}
|
||||
if (item.type === 'custom-report' && !customReportMap.has(item.meta.id)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}));
|
||||
|
||||
const layout = baseLayout;
|
||||
|
||||
@@ -331,8 +318,6 @@ export function Overview() {
|
||||
}}
|
||||
>
|
||||
{currentBreakpoint === 'desktop' && (
|
||||
<>
|
||||
{isDashboardsFeatureEnabled && (
|
||||
<>
|
||||
<Button
|
||||
ref={triggerRef}
|
||||
@@ -410,8 +395,6 @@ export function Overview() {
|
||||
]}
|
||||
/>
|
||||
</Popover>
|
||||
</>
|
||||
)}
|
||||
|
||||
{isEditing ? (
|
||||
<Button
|
||||
@@ -420,25 +403,15 @@ export function Overview() {
|
||||
>
|
||||
<Trans>Finish editing dashboard</Trans>
|
||||
</Button>
|
||||
) : isDashboardsFeatureEnabled ? (
|
||||
) : (
|
||||
<Button
|
||||
isDisabled={isImporting}
|
||||
onPress={() => setIsEditing(true)}
|
||||
>
|
||||
<Trans>Edit dashboard</Trans>
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
variant="primary"
|
||||
isDisabled={isImporting}
|
||||
onPress={() => navigate('/reports/custom')}
|
||||
>
|
||||
<Trans>Create new custom report</Trans>
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{isDashboardsFeatureEnabled && (
|
||||
<>
|
||||
<MenuButton
|
||||
ref={extraMenuTriggerRef}
|
||||
onPress={() => setExtraMenuOpen(true)}
|
||||
@@ -485,8 +458,6 @@ export function Overview() {
|
||||
</Popover>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
|
||||
@@ -6,7 +6,6 @@ import { Bar, BarChart, LabelList, ResponsiveContainer } from 'recharts';
|
||||
import { integerToCurrency } from 'loot-core/src/shared/util';
|
||||
import { type CashFlowWidget } from 'loot-core/src/types/models';
|
||||
|
||||
import { useFeatureFlag } from '../../../hooks/useFeatureFlag';
|
||||
import { theme } from '../../../style';
|
||||
import { View } from '../../common/View';
|
||||
import { PrivacyFilter } from '../../PrivacyFilter';
|
||||
@@ -98,7 +97,6 @@ export function CashFlowCard({
|
||||
onMetaChange,
|
||||
onRemove,
|
||||
}: CashFlowCardProps) {
|
||||
const isDashboardsFeatureEnabled = useFeatureFlag('dashboards');
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [start, end] = calculateTimeRange(meta?.timeFrame, defaultTimeFrame);
|
||||
@@ -121,11 +119,7 @@ export function CashFlowCard({
|
||||
return (
|
||||
<ReportCard
|
||||
isEditing={isEditing}
|
||||
to={
|
||||
isDashboardsFeatureEnabled
|
||||
? `/reports/cash-flow/${widgetId}`
|
||||
: '/reports/cash-flow'
|
||||
}
|
||||
to={`/reports/cash-flow/${widgetId}`}
|
||||
menuItems={[
|
||||
{
|
||||
name: 'rename',
|
||||
|
||||
@@ -11,7 +11,6 @@ import { type CustomReportEntity } from 'loot-core/types/models/reports';
|
||||
|
||||
import { useAccounts } from '../../../hooks/useAccounts';
|
||||
import { useCategories } from '../../../hooks/useCategories';
|
||||
import { useFeatureFlag } from '../../../hooks/useFeatureFlag';
|
||||
import { usePayees } from '../../../hooks/usePayees';
|
||||
import { useSyncedPref } from '../../../hooks/useSyncedPref';
|
||||
import { SvgExclamationSolid } from '../../../icons/v1';
|
||||
@@ -38,15 +37,9 @@ export function CustomReportListCards({
|
||||
report,
|
||||
onRemove,
|
||||
}: CustomReportListCardsProps) {
|
||||
const isDashboardsFeatureEnabled = useFeatureFlag('dashboards');
|
||||
|
||||
// It's possible for a dashboard to reference a non-existing
|
||||
// custom report
|
||||
if (!report) {
|
||||
if (!isDashboardsFeatureEnabled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<MissingReportCard isEditing={isEditing} onRemove={onRemove}>
|
||||
{t('This custom report has been deleted.')}
|
||||
|
||||
@@ -7,7 +7,6 @@ import {
|
||||
type NetWorthWidget,
|
||||
} from 'loot-core/src/types/models';
|
||||
|
||||
import { useFeatureFlag } from '../../../hooks/useFeatureFlag';
|
||||
import { styles } from '../../../style';
|
||||
import { Block } from '../../common/Block';
|
||||
import { View } from '../../common/View';
|
||||
@@ -40,7 +39,6 @@ export function NetWorthCard({
|
||||
onMetaChange,
|
||||
onRemove,
|
||||
}: NetWorthCardProps) {
|
||||
const isDashboardsFeatureEnabled = useFeatureFlag('dashboards');
|
||||
const { t } = useTranslation();
|
||||
const { isNarrowWidth } = useResponsive();
|
||||
|
||||
@@ -67,11 +65,7 @@ export function NetWorthCard({
|
||||
return (
|
||||
<ReportCard
|
||||
isEditing={isEditing}
|
||||
to={
|
||||
isDashboardsFeatureEnabled
|
||||
? `/reports/net-worth/${widgetId}`
|
||||
: '/reports/net-worth'
|
||||
}
|
||||
to={`/reports/net-worth/${widgetId}`}
|
||||
menuItems={[
|
||||
{
|
||||
name: 'rename',
|
||||
|
||||
@@ -13,7 +13,6 @@ import { amountToCurrency } from 'loot-core/src/shared/util';
|
||||
import { type SpendingWidget } from 'loot-core/types/models';
|
||||
import { type RuleConditionEntity } from 'loot-core/types/models/rule';
|
||||
|
||||
import { useFeatureFlag } from '../../../hooks/useFeatureFlag';
|
||||
import { useFilters } from '../../../hooks/useFilters';
|
||||
import { useNavigate } from '../../../hooks/useNavigate';
|
||||
import { theme, styles } from '../../../style';
|
||||
@@ -61,7 +60,6 @@ type SpendingInternalProps = {
|
||||
};
|
||||
|
||||
function SpendingInternal({ widget }: SpendingInternalProps) {
|
||||
const isDashboardsFeatureEnabled = useFeatureFlag('dashboards');
|
||||
const dispatch = useDispatch();
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -237,8 +235,6 @@ function SpendingInternal({ widget }: SpendingInternalProps) {
|
||||
>
|
||||
{!isNarrowWidth && (
|
||||
<SpaceBetween gap={0}>
|
||||
{isDashboardsFeatureEnabled && (
|
||||
<>
|
||||
<Button
|
||||
variant={isLive ? 'primary' : 'normal'}
|
||||
onPress={() => setIsLive(state => !state)}
|
||||
@@ -255,8 +251,6 @@ function SpendingInternal({ widget }: SpendingInternalProps) {
|
||||
marginLeft: 10,
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
<SpaceBetween gap={5}>
|
||||
<Text>
|
||||
|
||||
@@ -5,7 +5,6 @@ import * as monthUtils from 'loot-core/src/shared/months';
|
||||
import { amountToCurrency } from 'loot-core/src/shared/util';
|
||||
import { type SpendingWidget } from 'loot-core/src/types/models';
|
||||
|
||||
import { useFeatureFlag } from '../../../hooks/useFeatureFlag';
|
||||
import { styles } from '../../../style/styles';
|
||||
import { theme } from '../../../style/theme';
|
||||
import { Block } from '../../common/Block';
|
||||
@@ -35,7 +34,6 @@ export function SpendingCard({
|
||||
onMetaChange,
|
||||
onRemove,
|
||||
}: SpendingCardProps) {
|
||||
const isDashboardsFeatureEnabled = useFeatureFlag('dashboards');
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [compare, compareTo] = calculateSpendingReportTimeRange(meta ?? {});
|
||||
@@ -71,11 +69,7 @@ export function SpendingCard({
|
||||
return (
|
||||
<ReportCard
|
||||
isEditing={isEditing}
|
||||
to={
|
||||
isDashboardsFeatureEnabled
|
||||
? `/reports/spending/${widgetId}`
|
||||
: '/reports/spending'
|
||||
}
|
||||
to={`/reports/spending/${widgetId}`}
|
||||
menuItems={[
|
||||
{
|
||||
name: 'rename',
|
||||
|
||||
@@ -78,12 +78,6 @@ export function ExperimentalFeatures() {
|
||||
<FeatureToggle flag="goalTemplatesEnabled">
|
||||
<Trans>Goal templates</Trans>
|
||||
</FeatureToggle>
|
||||
<FeatureToggle
|
||||
flag="dashboards"
|
||||
feedbackLink="https://github.com/actualbudget/actual/issues/3282"
|
||||
>
|
||||
<Trans>Customizable reports page (dashboards)</Trans>
|
||||
</FeatureToggle>
|
||||
<FeatureToggle
|
||||
flag="actionTemplating"
|
||||
feedbackLink="https://github.com/actualbudget/actual/issues/3606"
|
||||
|
||||
@@ -4,7 +4,6 @@ import { useSyncedPref } from './useSyncedPref';
|
||||
|
||||
const DEFAULT_FEATURE_FLAG_STATE: Record<FeatureFlag, boolean> = {
|
||||
goalTemplatesEnabled: false,
|
||||
dashboards: false,
|
||||
actionTemplating: false,
|
||||
upcomingLengthAdjustment: false,
|
||||
contextMenus: false,
|
||||
|
||||
1
packages/loot-core/src/types/prefs.d.ts
vendored
@@ -1,5 +1,4 @@
|
||||
export type FeatureFlag =
|
||||
| 'dashboards'
|
||||
| 'goalTemplatesEnabled'
|
||||
| 'actionTemplating'
|
||||
| 'upcomingLengthAdjustment'
|
||||
|
||||
6
upcoming-release-notes/3856.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
category: Features
|
||||
authors: [MatissJanis]
|
||||
---
|
||||
|
||||
Dashboards: release as first party feature.
|
||||