Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
Expand Down Expand Up @@ -108,7 +108,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5

- name: Lint Dockerfile (dev)
uses: hadolint/hadolint-action@v3.1.0
Expand All @@ -130,7 +130,7 @@ jobs:
working-directory: frontend
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5

- name: Check if frontend exists
id: check_frontend
Expand All @@ -144,9 +144,9 @@ jobs:

- name: Setup Node.js
if: steps.check_frontend.outputs.exists == 'true'
uses: actions/setup-node@v4
uses: actions/setup-node@v5
with:
node-version: '20'
node-version: '24'
cache: 'npm'
cache-dependency-path: frontend/package-lock.json

Expand Down
4 changes: 2 additions & 2 deletions frontend/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>frontend</title>
<title>Signalist</title>
</head>
<body>
<div id="root"></div>
Expand Down
89 changes: 88 additions & 1 deletion frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,10 @@
"@mui/material": "^7.3.7",
"@tanstack/react-query": "^5.90.20",
"axios": "^1.13.4",
"i18next": "^25.8.18",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"react-i18next": "^16.5.8",
"react-router-dom": "^7.13.0"
},
"devDependencies": {
Expand Down
6 changes: 6 additions & 0 deletions frontend/public/favicon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 11 additions & 3 deletions frontend/src/api/articles.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import apiClient from './client';
import type { Article } from '../types';
import type { Article, PaginatedArticles } from '../types';

export interface ArticleFilters {
feedId?: string;
categoryId?: string;
isRead?: boolean;
search?: string;
page?: number;
limit?: number;
}

export async function getArticles(filters?: ArticleFilters): Promise<Article[]> {
export async function getArticles(filters?: ArticleFilters): Promise<PaginatedArticles> {
const params = new URLSearchParams();

if (filters?.feedId) {
Expand All @@ -23,11 +25,17 @@ export async function getArticles(filters?: ArticleFilters): Promise<Article[]>
if (filters?.search) {
params.append('search', filters.search);
}
if (filters?.page) {
params.append('page', String(filters.page));
}
if (filters?.limit) {
params.append('limit', String(filters.limit));
}

const queryString = params.toString();
const url = queryString ? `/articles?${queryString}` : '/articles';

const response = await apiClient.get<Article[]>(url);
const response = await apiClient.get<PaginatedArticles>(url);
return response.data;
}

Expand Down
26 changes: 17 additions & 9 deletions frontend/src/components/Article/ArticleCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import BookmarkBorderIcon from '@mui/icons-material/BookmarkBorder';
import BookmarkIcon from '@mui/icons-material/Bookmark';
import CheckCircleIcon from '@mui/icons-material/CheckCircle';
import CheckCircleOutlineIcon from '@mui/icons-material/CheckCircleOutline';
import { useTranslation } from 'react-i18next';
import type { Article } from '../../types';

interface ArticleCardProps {
Expand All @@ -25,14 +26,18 @@ export default function ArticleCard({
onToggleBookmark,
}: ArticleCardProps) {
const navigate = useNavigate();
const { t, i18n } = useTranslation();

const formatDate = (dateString: string | null) => {
if (!dateString) return '';
return new Date(dateString).toLocaleDateString('en-US', {
month: 'short',
day: 'numeric',
year: 'numeric',
});
return new Date(dateString).toLocaleDateString(
i18n.language === 'fr' ? 'fr-FR' : 'en-US',
{
month: 'short',
day: 'numeric',
year: 'numeric',
}
);
};

return (
Expand All @@ -57,7 +62,7 @@ export default function ArticleCard({
},
}}
>
<Box sx={{ flex: 1, minWidth: 0, display: 'flex', flexDirection: 'column', gap: 0.5 }}>
<Box sx={{ flex: 1, minWidth: 0, display: 'flex', flexDirection: 'column', gap: 0.5 }}>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<Typography
variant="overline"
Expand All @@ -76,6 +81,9 @@ export default function ArticleCard({
<Typography variant="overline" sx={{ color: 'text.secondary', lineHeight: 1 }}>
{article.feedTitle}
</Typography>
{isBookmarked && (
<BookmarkIcon sx={{ fontSize: 14, color: 'primary.main', ml: 'auto' }} />
)}
</Box>

<Typography
Expand Down Expand Up @@ -123,7 +131,7 @@ export default function ArticleCard({
className="article-actions"
sx={{ display: 'flex', gap: 0.5, mt: 'auto', opacity: 0, transition: 'opacity 0.15s ease' }}
>
<Tooltip title={article.isRead ? 'Mark as unread' : 'Mark as read'}>
<Tooltip title={article.isRead ? t('article.markAsUnread') : t('article.markAsRead')}>
<IconButton
size="small"
onClick={(e) => { e.stopPropagation(); onToggleRead(article.id, article.isRead); }}
Expand All @@ -132,7 +140,7 @@ export default function ArticleCard({
{article.isRead ? <CheckCircleIcon fontSize="small" /> : <CheckCircleOutlineIcon fontSize="small" />}
</IconButton>
</Tooltip>
<Tooltip title={isBookmarked ? 'Remove bookmark' : 'Add bookmark'}>
<Tooltip title={isBookmarked ? t('article.removeBookmark') : t('article.addBookmark')}>
<IconButton
size="small"
onClick={(e) => { e.stopPropagation(); onToggleBookmark(article.id, isBookmarked); }}
Expand All @@ -141,7 +149,7 @@ export default function ArticleCard({
{isBookmarked ? <BookmarkIcon fontSize="small" /> : <BookmarkBorderIcon fontSize="small" />}
</IconButton>
</Tooltip>
<Tooltip title="Open original article">
<Tooltip title={t('article.openOriginal')}>
<IconButton
size="small"
component="a"
Expand Down
Loading
Loading