feat: prepare project for cozy-ui migration (#157)

* feat: prepare project for cozy-ui migration

Build Configuration
- Add Stylus support via @rsbuild/plugin-stylus
- Add SVGR support via @rsbuild/plugin-svgr
- Update rsbuild.config.ts with custom configuration
- Add historyApiFallback for SPA routing

ESLint Configuration
- Create .eslintrc.json with react-app configuration
- Remove eslintConfig from package.json

CSS to Stylus Migration
Convert all 5 CSS files to Stylus (.styl) format:
  - src/App.css to src/App.styl
  - src/index.css to src/index.styl
  - src/components/Calendar/Calendar.css to src/components/Calendar/Calendar.styl
  - src/components/Calendar/CustomCalendar.css to src/components/Calendar/CustomCalendar.styl
  - src/components/Menubar/Menubar.css to src/components/Menubar/Menubar.styl
- Update all imports from .css to .styl
- Preserve all styling rules and values

MUI Styling Cleanup
- Replace all MUI sx props with standard style props across 15+ components
- Convert MUI spacing units (mr: 2 to marginRight: 16px)
- Convert MUI shorthand props (p: 2 to padding: 16px, mb: 1 to marginBottom: 8px)
- Remove MUI-specific syntax (&.Mui-checked selectors)
- Maintain exact visual appearance

FullCalendar Fixes
- Comment out problematic CSS rule in CustomCalendar.styl
- Fix display issues with timegrid slot labels

Files Modified
- Config: rsbuild.config.ts, .eslintrc.json, package.json
- Styling: 5 CSS to Stylus conversions, 15+ component sx to style updates
- Components: All components using MUI sx props updated

Development Workflow
- Continue using existing development commands
- Stylus files are now the source of truth for styling
- MUI components work with standard style props
- ESLint uses familiar react-app rules

Dependencies Added
- @rsbuild/plugin-stylus
- @rsbuild/plugin-svgr
This commit is contained in:
lenhanphung
2025-10-01 22:31:18 +07:00
committed by GitHub
parent 17c6bf7bce
commit 3870bceb6d
25 changed files with 1682 additions and 661 deletions
+27 -22
View File
@@ -203,7 +203,7 @@ export default function EventDisplayModal({
<MenuItem key={index} value={index}>
<Typography variant="body2">
<CircleIcon
sx={{
style={{
color: userPersonnalCalendars[index].color ?? "#3788D8",
width: 12,
height: 12,
@@ -217,7 +217,7 @@ export default function EventDisplayModal({
<MenuItem key={index} value={index}>
<Typography variant="body2">
<CircleIcon
sx={{
style={{
color: calendars[index].color ?? "#3788D8",
width: 12,
height: 12,
@@ -231,7 +231,7 @@ export default function EventDisplayModal({
return (
<Modal open={open} onClose={onClose}>
<Box
sx={{
style={{
position: "absolute",
top: "5vh",
left: "50%",
@@ -240,9 +240,9 @@ export default function EventDisplayModal({
maxHeight: "80vh",
}}
>
<Card sx={{ p: 2, position: "absolute" }}>
<Card style={{ padding: 16, position: "absolute" }}>
{/* Close button */}
<Box sx={{ position: "absolute", top: 8, right: 8 }}>
<Box style={{ position: "absolute", top: 8, right: 8 }}>
<IconButton
size="small"
onClick={() => onClose({}, "backdropClick")}
@@ -253,7 +253,7 @@ export default function EventDisplayModal({
<CardHeader title={isOwn ? "Edit Event" : "Event Details"} />
<CardContent sx={{ maxHeight: "75vh", overflow: "auto" }}>
<CardContent style={{ maxHeight: "75vh", overflow: "auto" }}>
{/* Title */}
<TextField
fullWidth
@@ -267,7 +267,7 @@ export default function EventDisplayModal({
{/* RSVP */}
{currentUserAttendee && isOwnCal && (
<Card sx={{ my: 1 }}>
<Card style={{ margin: "8px 0" }}>
<ButtonGroup size="small" fullWidth>
<Button
color={
@@ -408,7 +408,7 @@ export default function EventDisplayModal({
{/* Video */}
{event.x_openpass_videoconference && (
<InfoRow
icon={<VideocamIcon sx={{ fontSize: 18 }} />}
icon={<VideocamIcon style={{ fontSize: 18 }} />}
text="Video conference available"
data={event.x_openpass_videoconference}
/>
@@ -416,7 +416,7 @@ export default function EventDisplayModal({
{/* Attendees */}
{event.attendee?.length > 0 && (
<Box sx={{ mb: 1 }}>
<Box style={{ marginBottom: 8 }}>
<Typography variant="subtitle2">Attendees:</Typography>
{organizer.cal_address &&
renderAttendeeBadge(organizer, "org", true)}
@@ -444,7 +444,7 @@ export default function EventDisplayModal({
<Typography
variant="body2"
color="primary"
sx={{ cursor: "pointer", mt: 0.5 }}
style={{ cursor: "pointer", marginTop: 4 }}
onClick={() => setShowAllAttendees(!showAllAttendees)}
>
{showAllAttendees
@@ -457,7 +457,7 @@ export default function EventDisplayModal({
</Box>
)}
<Divider sx={{ my: 1 }} />
<Divider style={{ margin: "8px 0" }} />
{/* Extended options */}
{showMore && (
@@ -527,7 +527,10 @@ export default function EventDisplayModal({
{event.error && (
<InfoRow
icon={
<ErrorOutlineIcon color="error" sx={{ fontSize: 18 }} />
<ErrorOutlineIcon
color="error"
style={{ fontSize: 18 }}
/>
}
text={event.error}
error
@@ -581,7 +584,9 @@ export function InfoRow({
data?: string;
}) {
return (
<Box sx={{ display: "flex", alignItems: "center", gap: 1, mb: 1 }}>
<Box
style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 8 }}
>
{icon}
<Typography variant="body2" color={error ? "error" : "textPrimary"}>
{isValidUrl(data) ? <Link href={data}>{text}</Link> : text}
@@ -605,13 +610,13 @@ export function renderAttendeeBadge(
return (
<Box
key={key}
sx={{
style={{
display: "flex",
alignItems: "center",
gap: 1.5,
mb: 0.5,
p: 0.5,
borderRadius: 1,
gap: 12,
marginBottom: 4,
padding: 4,
borderRadius: 4,
}}
>
<Badge
@@ -620,7 +625,7 @@ export function renderAttendeeBadge(
badgeContent={
classIcon && (
<Box
sx={{
style={{
fontSize: 14,
lineHeight: 0,
backgroundColor: "white",
@@ -635,11 +640,11 @@ export function renderAttendeeBadge(
>
<Avatar {...stringAvatar(a.cn || a.cal_address)} />
</Badge>
<Box sx={{ display: "flex", flexDirection: "column", minWidth: 0 }}>
<Box style={{ display: "flex", flexDirection: "column", minWidth: 0 }}>
<Typography
variant="body2"
noWrap
sx={{
style={{
maxWidth: "180px",
overflow: "hidden",
textOverflow: "ellipsis",
@@ -651,7 +656,7 @@ export function renderAttendeeBadge(
<Typography
variant="caption"
color="text.secondary"
sx={{ fontStyle: "italic" }}
style={{ fontStyle: "italic" }}
>
Organizer
</Typography>
+25 -16
View File
@@ -115,15 +115,15 @@ export default function EventPreviewModal({
anchorPosition={anchorPosition ?? undefined}
onClose={onClose}
>
<Card sx={{ width: 300, p: 2, position: "relative" }}>
<Card style={{ width: 300, padding: 16, position: "relative" }}>
{/* Top-right buttons */}
<Box
sx={{
style={{
position: "absolute",
top: 8,
right: 8,
display: "flex",
gap: 1,
gap: 8,
}}
>
{(window as any).DEBUG && (
@@ -205,12 +205,12 @@ export default function EventPreviewModal({
</IconButton>
</Box>
<CardContent sx={{ pt: 1.5 }}>
<CardContent style={{ paddingTop: 12 }}>
{event.title && (
<Typography
variant="h6"
fontWeight="bold"
sx={{
style={{
wordBreak: "break-word",
}}
gutterBottom
@@ -230,7 +230,7 @@ export default function EventPreviewModal({
{/* Location */}
{event.location && (
<InfoRow
icon={<LocationOnIcon sx={{ fontSize: 18 }} />}
icon={<LocationOnIcon style={{ fontSize: 18 }} />}
text={event.location}
/>
)}
@@ -238,7 +238,7 @@ export default function EventPreviewModal({
{/* Video */}
{event.x_openpass_videoconference && (
<InfoRow
icon={<VideocamIcon sx={{ fontSize: 18 }} />}
icon={<VideocamIcon style={{ fontSize: 18 }} />}
text="Video conference available"
data={event.x_openpass_videoconference}
/>
@@ -246,7 +246,7 @@ export default function EventPreviewModal({
{/* Attendees */}
{event.attendee?.length > 0 && (
<Box sx={{ mb: 1 }}>
<Box style={{ marginBottom: 8 }}>
<Typography variant="subtitle2">Attendees:</Typography>
{organizer && renderAttendeeBadge(organizer, "org", true)}
{visibleAttendees.map((a, idx) =>
@@ -256,7 +256,7 @@ export default function EventPreviewModal({
<Typography
variant="body2"
color="primary"
sx={{ cursor: "pointer", mt: 0.5 }}
style={{ cursor: "pointer", marginTop: 4 }}
onClick={() => setShowAllAttendees(!showAllAttendees)}
>
{showAllAttendees
@@ -272,17 +272,26 @@ export default function EventPreviewModal({
{/* Error */}
{event.error && (
<InfoRow
icon={<ErrorOutlineIcon color="error" sx={{ fontSize: 18 }} />}
icon={
<ErrorOutlineIcon color="error" style={{ fontSize: 18 }} />
}
text={event.error}
error
/>
)}
{/* Calendar color dot */}
<Box sx={{ display: "flex", alignItems: "center", gap: 1, mb: 2 }}>
<CalendarTodayIcon sx={{ fontSize: 16 }} />
<Box
style={{
display: "flex",
alignItems: "center",
gap: 8,
marginBottom: 16,
}}
>
<CalendarTodayIcon style={{ fontSize: 16 }} />
<CircleIcon
sx={{
style={{
color: calendar.color ?? "#3788D8",
width: 12,
height: 12,
@@ -291,12 +300,12 @@ export default function EventPreviewModal({
<Typography variant="body2">{calendar.name}</Typography>
</Box>
<Divider sx={{ mb: 1 }} />
<Divider style={{ marginBottom: 8 }} />
{/* RSVP */}
{currentUserAttendee && (
<Box>
<Typography variant="body2" sx={{ mb: 1 }}>
<Typography variant="body2" style={{ marginBottom: 8 }}>
Will you attend?
</Typography>
<ButtonGroup size="small" fullWidth>
@@ -336,7 +345,7 @@ export default function EventPreviewModal({
{/* Description */}
{event.description && (
<Typography variant="body2" sx={{ mt: 1 }}>
<Typography variant="body2" style={{ marginTop: 8 }}>
{event.description}
</Typography>
)}
+1 -1
View File
@@ -190,7 +190,7 @@ function EventPopover({
<Card>
<CardHeader title={event?.uid ? "Duplicate Event" : "Create Event"} />
<CardContent
sx={{ maxHeight: "85vh", maxWidth: "40vw", overflow: "auto" }}
style={{ maxHeight: "85vh", maxWidth: "40vw", overflow: "auto" }}
>
<TextField
fullWidth