#708 apply strictier linting rules (#717)

* #708 apply strictier linting rules and fix simple eslint bugs

* #708 fix eslint errors relate to promise

* #708 fix eslint import/no-extraneous-dependencies

* #708 fix eslint errors of react-hook

* #708 enable eslint check for typescript

---------

Co-authored-by: lethemanh <lethemanh@lethemanhs-MacBook-Pro.local>
This commit is contained in:
lethemanh
2026-04-01 22:15:10 +07:00
committed by GitHub
parent 2bff6aae78
commit cadfa70e60
321 changed files with 27452 additions and 27600 deletions
+133 -136
View File
@@ -1,9 +1,9 @@
import { useAppDispatch, useAppSelector } from "@/app/hooks";
import { getCalendars } from "@/features/Calendars/CalendarApi";
import { Calendar } from "@/features/Calendars/CalendarTypes";
import { addSharedCalendarAsync } from "@/features/Calendars/services";
import { CalendarData } from "@/features/Calendars/types/CalendarData";
import { renameDefault } from "@/utils/renameDefault";
import { useAppDispatch, useAppSelector } from '@/app/hooks'
import { getCalendars } from '@/features/Calendars/CalendarApi'
import { Calendar } from '@/features/Calendars/CalendarTypes'
import { addSharedCalendarAsync } from '@/features/Calendars/services'
import { CalendarData } from '@/features/Calendars/types/CalendarData'
import { renameDefault } from '@/utils/renameDefault'
import {
Avatar,
Box,
@@ -11,61 +11,61 @@ import {
IconButton,
TextField,
Typography,
useTheme,
} from "@linagora/twake-mui";
import CloseIcon from "@mui/icons-material/Close";
import { useState } from "react";
import { useI18n } from "twake-i18n";
import { PeopleSearch, User } from "../Attendees/PeopleSearch";
import { ResponsiveDialog } from "../Dialog";
import { stringAvatar } from "../Event/utils/eventUtils";
import { ColorPicker } from "./CalendarColorPicker";
import { getAccessiblePair } from "@/utils/getAccessiblePair";
import { defaultColors } from "@/utils/defaultColors";
useTheme
} from '@linagora/twake-mui'
import CloseIcon from '@mui/icons-material/Close'
import { useState } from 'react'
import { useI18n } from 'twake-i18n'
import { PeopleSearch, User } from '../Attendees/PeopleSearch'
import { ResponsiveDialog } from '../Dialog'
import { stringAvatar } from '../Event/utils/eventUtils'
import { ColorPicker } from './CalendarColorPicker'
import { getAccessiblePair } from '@/utils/getAccessiblePair'
import { defaultColors } from '@/utils/defaultColors'
interface CalendarWithOwner {
cal: CalendarData;
owner: User;
cal: CalendarData
owner: User
}
function CalendarItem({
cal,
onRemove,
onColorChange,
onColorChange
}: {
cal: CalendarWithOwner;
onRemove: () => void;
onColorChange: (color: Record<string, string>) => void;
cal: CalendarWithOwner
onRemove: () => void
onColorChange: (color: Record<string, string>) => void
}) {
const theme = useTheme();
const { t } = useI18n();
const theme = useTheme()
const { t } = useI18n()
return (
<Box
key={cal.owner.email + cal.cal["dav:name"]}
key={cal.owner.email + cal.cal['dav:name']}
display="flex"
flexDirection="column"
alignItems="flex-start"
style={{
borderRadius: 8,
border: "1px solid #e5e7eb",
border: '1px solid #e5e7eb',
padding: 8,
marginBottom: 8,
marginBottom: 8
}}
>
<Box display="flex" alignItems="center" gap={2} mb={1}>
<Avatar
{...stringAvatar(cal.owner.displayName || cal.owner.email)}
style={{
border: `2px solid ${cal.cal["apple:color"] || defaultColors[0].light}`,
boxShadow: cal.cal["apple:color"]
? `0 0 0 2px ${cal.cal["apple:color"]}`
: `0 0 0 2px ${defaultColors[0].light}`,
border: `2px solid ${cal.cal['apple:color'] || defaultColors[0].light}`,
boxShadow: cal.cal['apple:color']
? `0 0 0 2px ${cal.cal['apple:color']}`
: `0 0 0 2px ${defaultColors[0].light}`
}}
/>
<Box>
<Typography variant="body1">
{renameDefault(
cal.cal["dav:name"],
cal.cal['dav:name'],
cal.owner.displayName,
t,
false
@@ -80,10 +80,10 @@ function CalendarItem({
<Box display="flex" alignItems="center" gap={1}>
<ColorPicker
selectedColor={{
light: cal.cal["apple:color"] ?? defaultColors[0].light,
dark: cal.cal["apple:color"]
? getAccessiblePair(cal.cal["apple:color"], theme)
: defaultColors[0].dark,
light: cal.cal['apple:color'] ?? defaultColors[0].light,
dark: cal.cal['apple:color']
? getAccessiblePair(cal.cal['apple:color'], theme)
: defaultColors[0].dark
}}
onChange={onColorChange}
/>
@@ -92,33 +92,30 @@ function CalendarItem({
</IconButton>
</Box>
</Box>
);
)
}
function SelectedCalendarsList({
calendars,
selectedCal,
onRemove,
onColorChange,
onColorChange
}: {
calendars: Record<string, Calendar>;
selectedCal: CalendarWithOwner[];
onRemove: (cal: CalendarWithOwner) => void;
onColorChange: (
cal: CalendarWithOwner,
color: Record<string, string>
) => void;
calendars: Record<string, Calendar>
selectedCal: CalendarWithOwner[]
onRemove: (cal: CalendarWithOwner) => void
onColorChange: (cal: CalendarWithOwner, color: Record<string, string>) => void
}) {
const { t } = useI18n();
if (selectedCal.length === 0) return null;
const { t } = useI18n()
if (selectedCal.length === 0) return null
const groupedByOwner = selectedCal.reduce<
Record<
string,
{
owner: User;
visibleCals: CalendarWithOwner[];
alreadyExisting: boolean;
owner: User
visibleCals: CalendarWithOwner[]
alreadyExisting: boolean
}
>
>((acc, cal) => {
@@ -126,107 +123,107 @@ function SelectedCalendarsList({
(existing: Calendar) =>
existing.id ===
cal.cal?._links?.self?.href
?.replace("/calendars/", "")
.replace(".json", "")
);
?.replace('/calendars/', '')
.replace('.json', '')
)
if (!acc[cal.owner.email]) {
acc[cal.owner.email] = {
owner: cal.owner,
visibleCals: [],
alreadyExisting: false,
};
alreadyExisting: false
}
}
if (exists) {
acc[cal.owner.email].alreadyExisting = true;
acc[cal.owner.email].alreadyExisting = true
} else {
acc[cal.owner.email].visibleCals.push(cal);
acc[cal.owner.email].visibleCals.push(cal)
}
return acc;
}, {});
return acc
}, {})
return (
<Box mt={2}>
<Typography variant="subtitle1" gutterBottom>
{t("common.name")}
{t('common.name')}
</Typography>
{Object.values(groupedByOwner).map(
({ owner, visibleCals, alreadyExisting }) => (
<Box key={owner.email} mb={2}>
{visibleCals.length > 0 ? (
visibleCals.map((cal) =>
visibleCals.map(cal =>
cal.cal ? (
<CalendarItem
key={cal.owner.email + cal.cal["dav:name"]}
key={cal.owner.email + cal.cal['dav:name']}
cal={cal}
onRemove={() => onRemove(cal)}
onColorChange={(color) => onColorChange(cal, color)}
onColorChange={color => onColorChange(cal, color)}
/>
) : (
<Typography
key={t("calendar.noPublicCalendarsFor", {
name: owner.displayName,
key={t('calendar.noPublicCalendarsFor', {
name: owner.displayName
})}
color="textSecondary"
>
{t("calendar.noPublicCalendarsFor", {
name: owner.displayName,
{t('calendar.noPublicCalendarsFor', {
name: owner.displayName
})}
</Typography>
)
)
) : alreadyExisting ? (
<Typography
key={t("calendar.noMoreCalendarsFor", {
name: owner.displayName,
key={t('calendar.noMoreCalendarsFor', {
name: owner.displayName
})}
color="textSecondary"
>
{t("calendar.noMoreCalendarsFor", { name: owner.displayName })}
{t('calendar.noMoreCalendarsFor', { name: owner.displayName })}
</Typography>
) : null}
</Box>
)
)}
</Box>
);
)
}
export default function CalendarSearch({
open,
onClose,
onClose
}: {
open: boolean;
open: boolean
onClose: (
result?: string[] | Record<string, never>,
reason?: "backdropClick" | "escapeKeyDown"
) => void;
reason?: 'backdropClick' | 'escapeKeyDown'
) => void
}) {
const dispatch = useAppDispatch();
const theme = useTheme();
const dispatch = useAppDispatch()
const theme = useTheme()
const openpaasId =
useAppSelector((state) => state.user.userData?.openpaasId) ?? "";
const calendars = useAppSelector((state) => state.calendars.list);
useAppSelector(state => state.user.userData?.openpaasId) ?? ''
const calendars = useAppSelector(state => state.calendars.list)
const [selectedCal, setSelectedCalendars] = useState<CalendarWithOwner[]>([]);
const [selectedUsers, setSelectedUsers] = useState<User[]>([]);
const [selectedCal, setSelectedCalendars] = useState<CalendarWithOwner[]>([])
const [selectedUsers, setSelectedUsers] = useState<User[]>([])
const handleSave = async () => {
if (selectedCal.length > 0) {
const idList = await Promise.all(
selectedCal.map(async (cal) => {
const calId = crypto.randomUUID();
selectedCal.map(async cal => {
const calId = crypto.randomUUID()
const exists = Object.values(calendars).some(
(existing: Calendar) =>
existing.id ===
cal.cal?._links?.self?.href
?.replace("/calendars/", "")
.replace(".json", "")
);
?.replace('/calendars/', '')
.replace('.json', '')
)
if (!exists && cal.cal) {
await dispatch(
addSharedCalendarAsync({
@@ -234,117 +231,117 @@ export default function CalendarSearch({
calId,
cal: {
...cal,
color: cal.cal["apple:color"]
color: cal.cal['apple:color']
? {
light: cal.cal["apple:color"],
dark: getAccessiblePair(cal.cal["apple:color"], theme),
light: cal.cal['apple:color'],
dark: getAccessiblePair(cal.cal['apple:color'], theme)
}
: defaultColors[0],
},
: defaultColors[0]
}
})
);
)
return cal.cal._links.self?.href
?.replace("/calendars/", "")
.replace(".json", "");
?.replace('/calendars/', '')
.replace('.json', '')
}
return null;
return null
})
);
)
onClose(idList.filter(Boolean) as string[]);
setSelectedCalendars([]);
setSelectedUsers([]);
onClose(idList.filter(Boolean) as string[])
setSelectedCalendars([])
setSelectedUsers([])
}
};
const { t } = useI18n();
}
const { t } = useI18n()
return (
<ResponsiveDialog
open={open}
contentSx={{ paddingTop: "8px !important" }}
contentSx={{ paddingTop: '8px !important' }}
onClose={() => {
onClose({}, "backdropClick");
setSelectedCalendars([]);
setSelectedUsers([]);
onClose({}, 'backdropClick')
setSelectedCalendars([])
setSelectedUsers([])
}}
title={t("calendar.browseOtherCalendars")}
title={t('calendar.browseOtherCalendars')}
actions={
<>
<Button
variant="outlined"
onClick={() => onClose({}, "backdropClick")}
onClick={() => onClose({}, 'backdropClick')}
>
{t("common.cancel")}
{t('common.cancel')}
</Button>
<Button variant="contained" onClick={handleSave}>
{t("actions.add")}
{t('actions.add')}
</Button>
</>
}
>
<PeopleSearch
objectTypes={["user"]}
objectTypes={['user']}
selectedUsers={selectedUsers}
inputSlot={(params) => <TextField {...params} size="small" />}
inputSlot={params => <TextField {...params} size="small" />}
onChange={async (_event: React.SyntheticEvent, value: User[]) => {
setSelectedUsers(value);
setSelectedUsers(value)
const cals = await Promise.all(
value.map(async (user: User) => {
if (user?.openpaasId) {
const cals = await getCalendars(
user.openpaasId,
"sharedPublic=true&"
);
return cals._embedded?.["dav:calendar"]
? cals._embedded["dav:calendar"].map((cal) => ({
'sharedPublic=true&'
)
return cals._embedded?.['dav:calendar']
? cals._embedded['dav:calendar'].map(cal => ({
cal,
owner: user,
owner: user
}))
: { cal: undefined, owner: user };
: { cal: undefined, owner: user }
}
return null;
return null
})
);
)
setSelectedCalendars(
cals.flat().filter(Boolean) as CalendarWithOwner[]
);
)
}}
/>
<SelectedCalendarsList
calendars={calendars}
selectedCal={selectedCal}
onRemove={(cal) => {
if (!cal.cal?._links?.self?.href) return;
setSelectedCalendars((prev) =>
onRemove={cal => {
if (!cal.cal?._links?.self?.href) return
setSelectedCalendars(prev =>
prev.filter(
(c) => c.cal?._links?.self?.href !== cal.cal._links.self?.href
c => c.cal?._links?.self?.href !== cal.cal._links.self?.href
)
);
)
if (
!selectedCal.find(
(c) =>
c =>
cal.owner.email === c.owner.email &&
c.cal?._links?.self?.href !== cal.cal._links.self?.href
)
) {
setSelectedUsers((prev) =>
prev.filter((u) => u.email !== cal.owner.email)
);
setSelectedUsers(prev =>
prev.filter(u => u.email !== cal.owner.email)
)
}
}}
onColorChange={(cal, color) =>
setSelectedCalendars((prev) =>
prev.map((prevcal) =>
setSelectedCalendars(prev =>
prev.map(prevcal =>
prevcal.owner.email === cal.owner.email &&
prevcal.cal._links.self?.href === cal.cal._links.self?.href
? {
...prevcal,
cal: {
...prevcal.cal,
"apple:color": color.light,
},
'apple:color': color.light
}
}
: prevcal
)
@@ -352,5 +349,5 @@ export default function CalendarSearch({
}
/>
</ResponsiveDialog>
);
)
}