Skip to content

Commit

Permalink
feat(web): finish initial mobile optimization for list components
Browse files Browse the repository at this point in the history
  • Loading branch information
mikonse committed Jan 2, 2025
1 parent 77897a7 commit 8994275
Show file tree
Hide file tree
Showing 21 changed files with 170 additions and 153 deletions.
3 changes: 2 additions & 1 deletion frontend/apps/web/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@
"options": {
"buildTarget": "web:build",
"hmr": true,
"host": "0.0.0.0"
"host": "0.0.0.0",
"proxyConfig": "apps/web/proxy.conf.json"
},
"configurations": {
"development": {
Expand Down
7 changes: 7 additions & 0 deletions frontend/apps/web/proxy.conf.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[
{
"context": ["/api"],
"target": "http://localhost:8080",
"secure": false
}
]
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ export const AuthenticatedLayout: React.FC = () => {
};

return (
<Box sx={{ display: "flex" }}>
<Box sx={{ display: "flex", overflow: "hidden" }}>
<CssBaseline />
<AppBar
position="fixed"
Expand Down Expand Up @@ -284,12 +284,12 @@ export const AuthenticatedLayout: React.FC = () => {
component="main"
sx={{
flexGrow: 1,
width: { sm: `calc(100% - ${drawerWidth}px)` },
width: { sm: `calc(100vw - ${drawerWidth}px)`, md: "100vw" },
}}
>
<Toolbar />
<Banner />
<Container maxWidth="lg" sx={{ padding: { xs: 0, md: 1, lg: 3 } }}>
<Container maxWidth="lg" sx={{ padding: { xs: 0, md: 1, lg: 3 }, overflow: "auto" }}>
<React.Suspense fallback={<Loading />}>
<Outlet />
</React.Suspense>
Expand Down
4 changes: 2 additions & 2 deletions frontend/apps/web/src/components/AccountSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ export const AccountSelect: React.FC<AccountSelectProps> = ({
disableClearable
className={className}
onChange={(event, newValue: Account) => onChange(newValue)}
renderOption={(props, account) => (
<Box component="li" {...props}>
renderOption={({ key, ...props }, account) => (
<Box component="li" key={key} {...props}>
{getAccountIcon(account.type)}
<Box sx={{ ml: 1, display: "flex", flexDirection: "column" }}>
<Typography variant="body2" component="span">
Expand Down
59 changes: 40 additions & 19 deletions frontend/apps/web/src/components/ShareSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ export const ShareSelect: React.FC<ShareSelectProps> = ({
}
return nAccs;
}, 0);
const nSelected = nSelectedEvents + nSelectedPeople;
const showSearch = !isSmallScreen && unfilteredAccounts.length > 5;

const handleAccountShareChange = (accountId: number, shareValue: number) => {
Expand All @@ -207,9 +208,21 @@ export const ShareSelect: React.FC<ShareSelectProps> = ({
}
};

const handleSelectAll = () => {
if (nSelected === accounts.length) {
onChange?.({});
} else {
const newVal: TransactionShare = accounts.reduce<TransactionShare>((shares, account) => {
shares[account.id] = 1;
return shares;
}, {});
onChange?.(newVal);
}
};

return (
<div>
<Grid container direction="row" justifyContent="space-between">
<Grid container direction={isSmallScreen ? "column" : "row"} justifyContent="space-between">
<Box sx={{ display: "flex", flexDirection: "row", alignItems: "center", gap: "0.5em", marginY: 1 }}>
<Typography variant="subtitle1">{label}</Typography>
{nSelectedPeople > 0 && (
Expand Down Expand Up @@ -284,28 +297,36 @@ export const ShareSelect: React.FC<ShareSelectProps> = ({
value={searchValue}
onChange={(e) => setSearchValue(e.target.value)}
variant="standard"
InputProps={{
startAdornment: (
<InputAdornment position="start">
<SearchIcon />
</InputAdornment>
),
endAdornment: (
<InputAdornment position="end">
<IconButton
aria-label="clear search input"
onClick={(e) => setSearchValue("")}
edge="end"
>
<ClearIcon />
</IconButton>
</InputAdornment>
),
slotProps={{
input: {
startAdornment: (
<InputAdornment position="start">
<SearchIcon />
</InputAdornment>
),
endAdornment: (
<InputAdornment position="end">
<IconButton
aria-label="clear search input"
onClick={(e) => setSearchValue("")}
edge="end"
>
<ClearIcon />
</IconButton>
</InputAdornment>
),
},
}}
/>
)}
</TableCell>
<TableCell width="100px">{t("common.shares")}</TableCell>
<TableCell width="100px">
<FormControlLabel
control={<Checkbox onChange={handleSelectAll} />}
checked={nSelected === accounts.length}
label={t("common.shares")}
/>
</TableCell>
{additionalShareInfoHeader ?? null}
</TableRow>
</TableHead>
Expand Down
16 changes: 3 additions & 13 deletions frontend/apps/web/src/components/accounts/BalanceTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,14 @@ export const BalanceTable: React.FC<Props> = ({ group }) => {
return {
id: acc.id,
name: acc.name,
description: acc.description,
balance: balances[acc.id]?.balance ?? 0,
totalPaid: balances[acc.id]?.totalPaid ?? 0,
totalConsumed: balances[acc.id]?.totalConsumed ?? 0,
};
});

const columns: GridColDef[] = [
{ field: "id", headerName: "ID" },
{ field: "name", headerName: "Name", width: 150 },
{ field: "description", headerName: "Description", width: 200 },
{ field: "name", headerName: "Name", flex: 1 },
{
field: "totalConsumed",
headerName: t("balanceTable.totalConsumed"),
Expand All @@ -48,20 +45,13 @@ export const BalanceTable: React.FC<Props> = ({ group }) => {
];

return (
<div style={{ width: "100%" }}>
<div style={{ width: "100%", display: "flex", flexDirection: "column" }}>
<DataGrid
getRowId={(row) => row.id}
sx={{ border: 0 }}
rows={tableData}
initialState={{
columns: {
columnVisibilityModel: {
id: false,
},
},
}}
columns={columns}
disableRowSelectionOnClick
autoHeight
slots={{
toolbar: GridToolbar,
}}
Expand Down
8 changes: 4 additions & 4 deletions frontend/apps/web/src/pages/accounts/Balances.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,8 @@ export const Balances: React.FC<Props> = ({ groupId }) => {
{isSmallScreen ? (
<List>
{personalAccounts.map((account) => (
<>
<ListItemLink key={account.id} to={`/groups/${group.id}/accounts/${account.id}`}>
<div key={account.id}>
<ListItemLink to={`/groups/${group.id}/accounts/${account.id}`}>
<ListItemText primary={account.name} />
<Typography
align="right"
Expand All @@ -106,8 +106,8 @@ export const Balances: React.FC<Props> = ({ groupId }) => {
{formatCurrency(balances[account.id]?.balance, group.currency_symbol)}
</Typography>
</ListItemLink>
<Divider key={account.id * 2} component="li" />
</>
<Divider component="li" />
</div>
))}
</List>
) : (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,13 +104,14 @@ export const ClearingAccountList: React.FC<Props> = ({ groupId }) => {
}}
startAdornment={
<InputAdornment position="start">
<SearchIcon sx={{ color: "action.active" }} />
<SearchIcon />
</InputAdornment>
}
endAdornment={
<InputAdornment position="end">
<IconButton
aria-label="clear search input"
sx={{ padding: 0, margin: 0 }}
onClick={() => setSearchValue("")}
edge="end"
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,13 +105,14 @@ export const PersonalAccountList: React.FC<Props> = ({ groupId }) => {
}}
startAdornment={
<InputAdornment position="start">
<SearchIcon sx={{ color: "action.active" }} />
<SearchIcon />
</InputAdornment>
}
endAdornment={
<InputAdornment position="end">
<IconButton
aria-label="clear search input"
sx={{ padding: 0, margin: 0 }}
onClick={() => setSearchValue("")}
edge="end"
>
Expand Down
16 changes: 9 additions & 7 deletions frontend/apps/web/src/pages/accounts/SettlementPlanDisplay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
selectSettlementPlan,
useGroupCurrencySymbol,
} from "@abrechnung/redux";
import { Button, List, ListItem, ListItemSecondaryAction, ListItemText, Typography } from "@mui/material";
import { Button, List, ListItem, ListItemText, Typography } from "@mui/material";
import * as React from "react";
import { Navigate, useNavigate } from "react-router";
import { useTranslation } from "react-i18next";
Expand Down Expand Up @@ -54,7 +54,14 @@ export const SettlementPlanDisplay: React.FC<Props> = ({ groupId }) => {
<Typography variant="h5">{t("accounts.settlement.title")}</Typography>
<List>
{settlementPlan.map((planItem) => (
<ListItem key={`${planItem.creditorId}-${planItem.debitorId}`}>
<ListItem
key={`${planItem.creditorId}-${planItem.debitorId}`}
secondaryAction={
<Button onClick={() => onSettleClicked(planItem)}>
{t("accounts.settlement.settleButton")}
</Button>
}
>
<ListItemText
primary={
<span>
Expand All @@ -66,11 +73,6 @@ export const SettlementPlanDisplay: React.FC<Props> = ({ groupId }) => {
</span>
}
/>
<ListItemSecondaryAction>
<Button onClick={() => onSettleClicked(planItem)}>
{t("accounts.settlement.settleButton")}
</Button>
</ListItemSecondaryAction>
</ListItem>
))}
</List>
Expand Down
74 changes: 19 additions & 55 deletions frontend/apps/web/src/pages/groups/GroupList.tsx
Original file line number Diff line number Diff line change
@@ -1,51 +1,30 @@
import React, { useState } from "react";
import { GroupCreateModal } from "@/components/groups/GroupCreateModal";
import { GroupDeleteModal } from "@/components/groups/GroupDeleteModal";
import {
Alert,
Grid,
IconButton,
List,
ListItem,
ListItemSecondaryAction,
ListItemText,
Stack,
Typography,
} from "@mui/material";
import { Add, Delete } from "@mui/icons-material";
import { Alert, Divider, Grid, IconButton, List, ListItem, ListItemText, Stack, Typography } from "@mui/material";
import { Add } from "@mui/icons-material";
import { MobilePaper, ListItemLink } from "@/components/style";
import { selectIsGuestUser, selectGroups } from "@abrechnung/redux";
import { useAppSelector } from "@/store";
import { useTitle } from "@/core/utils";
import { useTranslation } from "react-i18next";
import { Group } from "@abrechnung/api";
import { useIsSmallScreen } from "@/hooks";
import { DateTime } from "luxon";

const GList: React.FC<{ groups: Group[] }> = ({ groups }) => {
const { t } = useTranslation();
const [groupToDelete, setGroupToDelete] = useState<Group | null>(null);

const openGroupDeletionModal = (groupID: number) => {
const g = groups.find((group) => group.id === groupID);
if (g) {
setGroupToDelete(g);
}
};

const closeGroupDeletionModal = () => {
setGroupToDelete(null);
};
const isSmallScreen = useIsSmallScreen();

return (
<>
<List>
{groups.length === 0 ? (
<ListItem key={0}>
<span>{t("groups.list.noGroups")}</span>
</ListItem>
) : (
groups.map((group) => {
return (
<List>
{groups.length === 0 ? (
<ListItem>
<span>{t("groups.list.noGroups")}</span>
</ListItem>
) : (
groups.map((group) => {
return (
<>
<ListItemLink sx={{ padding: 0 }} key={group.id} to={`/groups/${group.id}`}>
<ListItemText
primary={group.name}
Expand All @@ -57,36 +36,21 @@ const GList: React.FC<{ groups: Group[] }> = ({ groups }) => {
<br />
</>
)}
{t("common.lastChangedWithTime", {
{t("groups.list.lastUpdateAt", {
datetime: DateTime.fromISO(group.last_changed).toLocaleString(
DateTime.DATETIME_FULL
),
})}
</>
}
/>
<ListItemSecondaryAction>
<IconButton
edge="end"
aria-label="delete-group"
onClick={() => openGroupDeletionModal(group.id)}
>
<Delete />
</IconButton>
</ListItemSecondaryAction>
</ListItemLink>
);
})
)}
</List>
{groupToDelete != null && (
<GroupDeleteModal
show={groupToDelete != null}
onClose={closeGroupDeletionModal}
groupToDelete={groupToDelete}
/>
{isSmallScreen && <Divider component="li" />}
</>
);
})
)}
</>
</List>
);
};

Expand Down
Loading

0 comments on commit 8994275

Please sign in to comment.