Fix
This commit is contained in:
		
							
								
								
									
										28
									
								
								frontend/eslint.config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								frontend/eslint.config.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,28 @@
 | 
				
			|||||||
 | 
					import js from '@eslint/js'
 | 
				
			||||||
 | 
					import globals from 'globals'
 | 
				
			||||||
 | 
					import reactHooks from 'eslint-plugin-react-hooks'
 | 
				
			||||||
 | 
					import reactRefresh from 'eslint-plugin-react-refresh'
 | 
				
			||||||
 | 
					import tseslint from 'typescript-eslint'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default tseslint.config(
 | 
				
			||||||
 | 
					  { ignores: ['dist'] },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    extends: [js.configs.recommended, ...tseslint.configs.recommended],
 | 
				
			||||||
 | 
					    files: ['**/*.{ts,tsx}'],
 | 
				
			||||||
 | 
					    languageOptions: {
 | 
				
			||||||
 | 
					      ecmaVersion: 2020,
 | 
				
			||||||
 | 
					      globals: globals.browser,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    plugins: {
 | 
				
			||||||
 | 
					      'react-hooks': reactHooks,
 | 
				
			||||||
 | 
					      'react-refresh': reactRefresh,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    rules: {
 | 
				
			||||||
 | 
					      ...reactHooks.configs.recommended.rules,
 | 
				
			||||||
 | 
					      'react-refresh/only-export-components': [
 | 
				
			||||||
 | 
					        'warn',
 | 
				
			||||||
 | 
					        { allowConstantExport: true },
 | 
				
			||||||
 | 
					      ],
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
@@ -3,6 +3,7 @@ import "./app/globals.css";
 | 
				
			|||||||
import Dashboard from "./components/Dashboard";
 | 
					import Dashboard from "./components/Dashboard";
 | 
				
			||||||
import EpisodePlayer from "./components/EpisodePlayer";
 | 
					import EpisodePlayer from "./components/EpisodePlayer";
 | 
				
			||||||
import FeedManager from "./components/FeedManager";
 | 
					import FeedManager from "./components/FeedManager";
 | 
				
			||||||
 | 
					import React from "react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type TabType = "dashboard" | "episodes" | "feeds";
 | 
					type TabType = "dashboard" | "episodes" | "feeds";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,6 +2,7 @@ import { useState } from "react";
 | 
				
			|||||||
import FeedManager from "../components/FeedManager";
 | 
					import FeedManager from "../components/FeedManager";
 | 
				
			||||||
import EpisodePlayer from "../components/EpisodePlayer";
 | 
					import EpisodePlayer from "../components/EpisodePlayer";
 | 
				
			||||||
import Dashboard from "../components/Dashboard";
 | 
					import Dashboard from "../components/Dashboard";
 | 
				
			||||||
 | 
					import React from "react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const metadata = {
 | 
					export const metadata = {
 | 
				
			||||||
  title: "Voice RSS Summary",
 | 
					  title: "Voice RSS Summary",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,5 @@
 | 
				
			|||||||
import { useEffect, useState } from "react";
 | 
					import { useEffect, useState } from "react";
 | 
				
			||||||
 | 
					import React from "react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface Stats {
 | 
					interface Stats {
 | 
				
			||||||
  totalFeeds: number;
 | 
					  totalFeeds: number;
 | 
				
			||||||
@@ -66,6 +67,7 @@ export default function Dashboard() {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
    } catch (error) {
 | 
					    } catch (error) {
 | 
				
			||||||
      alert("エラーが発生しました。");
 | 
					      alert("エラーが発生しました。");
 | 
				
			||||||
 | 
					      console.error("Batch process trigger error:", error);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,5 @@
 | 
				
			|||||||
import { useEffect, useState, useRef } from "react";
 | 
					import { useEffect, useState, useRef } from "react";
 | 
				
			||||||
 | 
					import React from "react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface Episode {
 | 
					interface Episode {
 | 
				
			||||||
  id: string;
 | 
					  id: string;
 | 
				
			||||||
@@ -55,7 +56,7 @@ export default function EpisodePlayer() {
 | 
				
			|||||||
      audio.removeEventListener("loadedmetadata", updateDuration);
 | 
					      audio.removeEventListener("loadedmetadata", updateDuration);
 | 
				
			||||||
      audio.removeEventListener("ended", handleEnded);
 | 
					      audio.removeEventListener("ended", handleEnded);
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
  }, [selectedEpisode]);
 | 
					  }, [selectedEpisode, isPlaying, audioRef, currentTime, duration]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const fetchEpisodes = async () => {
 | 
					  const fetchEpisodes = async () => {
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,5 @@
 | 
				
			|||||||
import { useEffect, useState } from "react";
 | 
					import { useEffect, useState } from "react";
 | 
				
			||||||
 | 
					import React from "react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface FeedItem {
 | 
					interface FeedItem {
 | 
				
			||||||
  id: string;
 | 
					  id: string;
 | 
				
			||||||
@@ -18,12 +19,15 @@ interface FeedListProps {
 | 
				
			|||||||
  categoryFilter?: string;
 | 
					  categoryFilter?: string;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function FeedList({ searchTerm = "", categoryFilter = "" }: FeedListProps = {}) {
 | 
					export default function FeedList({
 | 
				
			||||||
 | 
					  searchTerm = "",
 | 
				
			||||||
 | 
					  categoryFilter = "",
 | 
				
			||||||
 | 
					}: FeedListProps = {}) {
 | 
				
			||||||
  const [feeds, setFeeds] = useState<FeedItem[]>([]);
 | 
					  const [feeds, setFeeds] = useState<FeedItem[]>([]);
 | 
				
			||||||
  const [loading, setLoading] = useState(true);
 | 
					  const [loading, setLoading] = useState(true);
 | 
				
			||||||
  const [error, setError] = useState<string | null>(null);
 | 
					  const [error, setError] = useState<string | null>(null);
 | 
				
			||||||
  const [sortBy, setSortBy] = useState<'date' | 'title'>('date');
 | 
					  const [sortBy, setSortBy] = useState<"date" | "title">("date");
 | 
				
			||||||
  const [sortOrder, setSortOrder] = useState<'asc' | 'desc'>('desc');
 | 
					  const [sortOrder, setSortOrder] = useState<"asc" | "desc">("desc");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  useEffect(() => {
 | 
					  useEffect(() => {
 | 
				
			||||||
    fetchFeeds();
 | 
					    fetchFeeds();
 | 
				
			||||||
@@ -47,43 +51,48 @@ export default function FeedList({ searchTerm = "", categoryFilter = "" }: FeedL
 | 
				
			|||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const filteredAndSortedFeeds = feeds
 | 
					  const filteredAndSortedFeeds = feeds
 | 
				
			||||||
    .filter(feed => {
 | 
					    .filter((feed) => {
 | 
				
			||||||
      const matchesSearch = !searchTerm || 
 | 
					      const matchesSearch =
 | 
				
			||||||
 | 
					        !searchTerm ||
 | 
				
			||||||
        feed.title.toLowerCase().includes(searchTerm.toLowerCase()) ||
 | 
					        feed.title.toLowerCase().includes(searchTerm.toLowerCase()) ||
 | 
				
			||||||
        feed.contentSnippet?.toLowerCase().includes(searchTerm.toLowerCase()) ||
 | 
					        feed.contentSnippet?.toLowerCase().includes(searchTerm.toLowerCase()) ||
 | 
				
			||||||
        feed.source?.title?.toLowerCase().includes(searchTerm.toLowerCase());
 | 
					        feed.source?.title?.toLowerCase().includes(searchTerm.toLowerCase());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const matchesCategory = !categoryFilter || feed.category === categoryFilter;
 | 
					      const matchesCategory =
 | 
				
			||||||
 | 
					        !categoryFilter || feed.category === categoryFilter;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      return matchesSearch && matchesCategory;
 | 
					      return matchesSearch && matchesCategory;
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
    .sort((a, b) => {
 | 
					    .sort((a, b) => {
 | 
				
			||||||
      const multiplier = sortOrder === 'asc' ? 1 : -1;
 | 
					      const multiplier = sortOrder === "asc" ? 1 : -1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (sortBy === 'date') {
 | 
					      if (sortBy === "date") {
 | 
				
			||||||
        return (new Date(a.pubDate).getTime() - new Date(b.pubDate).getTime()) * multiplier;
 | 
					        return (
 | 
				
			||||||
 | 
					          (new Date(a.pubDate).getTime() - new Date(b.pubDate).getTime()) *
 | 
				
			||||||
 | 
					          multiplier
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        return a.title.localeCompare(b.title) * multiplier;
 | 
					        return a.title.localeCompare(b.title) * multiplier;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const handleSort = (field: 'date' | 'title') => {
 | 
					  const handleSort = (field: "date" | "title") => {
 | 
				
			||||||
    if (sortBy === field) {
 | 
					    if (sortBy === field) {
 | 
				
			||||||
      setSortOrder(sortOrder === 'asc' ? 'desc' : 'asc');
 | 
					      setSortOrder(sortOrder === "asc" ? "desc" : "asc");
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      setSortBy(field);
 | 
					      setSortBy(field);
 | 
				
			||||||
      setSortOrder('desc');
 | 
					      setSortOrder("desc");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const formatDate = (dateString: string) => {
 | 
					  const formatDate = (dateString: string) => {
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      return new Date(dateString).toLocaleDateString('ja-JP', {
 | 
					      return new Date(dateString).toLocaleDateString("ja-JP", {
 | 
				
			||||||
        year: 'numeric',
 | 
					        year: "numeric",
 | 
				
			||||||
        month: 'short',
 | 
					        month: "short",
 | 
				
			||||||
        day: 'numeric',
 | 
					        day: "numeric",
 | 
				
			||||||
        hour: '2-digit',
 | 
					        hour: "2-digit",
 | 
				
			||||||
        minute: '2-digit'
 | 
					        minute: "2-digit",
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    } catch {
 | 
					    } catch {
 | 
				
			||||||
      return dateString;
 | 
					      return dateString;
 | 
				
			||||||
@@ -94,7 +103,10 @@ export default function FeedList({ searchTerm = "", categoryFilter = "" }: FeedL
 | 
				
			|||||||
    return (
 | 
					    return (
 | 
				
			||||||
      <div className="space-y-6">
 | 
					      <div className="space-y-6">
 | 
				
			||||||
        {[...Array(5)].map((_, i) => (
 | 
					        {[...Array(5)].map((_, i) => (
 | 
				
			||||||
          <div key={i} className="glass-effect rounded-3xl p-6 border border-white/20 animate-pulse">
 | 
					          <div
 | 
				
			||||||
 | 
					            key={i}
 | 
				
			||||||
 | 
					            className="glass-effect rounded-3xl p-6 border border-white/20 animate-pulse"
 | 
				
			||||||
 | 
					          >
 | 
				
			||||||
            <div className="flex items-start space-x-4">
 | 
					            <div className="flex items-start space-x-4">
 | 
				
			||||||
              <div className="w-16 h-16 bg-slate-200 rounded-2xl"></div>
 | 
					              <div className="w-16 h-16 bg-slate-200 rounded-2xl"></div>
 | 
				
			||||||
              <div className="flex-1 space-y-3">
 | 
					              <div className="flex-1 space-y-3">
 | 
				
			||||||
@@ -118,12 +130,11 @@ export default function FeedList({ searchTerm = "", categoryFilter = "" }: FeedL
 | 
				
			|||||||
            ⚠️
 | 
					            ⚠️
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
          <div>
 | 
					          <div>
 | 
				
			||||||
            <h3 className="text-lg font-bold text-red-800">エラーが発生しました</h3>
 | 
					            <h3 className="text-lg font-bold text-red-800">
 | 
				
			||||||
 | 
					              エラーが発生しました
 | 
				
			||||||
 | 
					            </h3>
 | 
				
			||||||
            <p className="text-red-700">{error}</p>
 | 
					            <p className="text-red-700">{error}</p>
 | 
				
			||||||
            <button 
 | 
					            <button onClick={fetchFeeds} className="mt-3 btn-primary text-sm">
 | 
				
			||||||
              onClick={fetchFeeds}
 | 
					 | 
				
			||||||
              className="mt-3 btn-primary text-sm"
 | 
					 | 
				
			||||||
            >
 | 
					 | 
				
			||||||
              再読み込み
 | 
					              再読み込み
 | 
				
			||||||
            </button>
 | 
					            </button>
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
@@ -137,36 +148,34 @@ export default function FeedList({ searchTerm = "", categoryFilter = "" }: FeedL
 | 
				
			|||||||
      {/* Sort Controls */}
 | 
					      {/* Sort Controls */}
 | 
				
			||||||
      <div className="glass-effect rounded-2xl p-4 border border-white/20">
 | 
					      <div className="glass-effect rounded-2xl p-4 border border-white/20">
 | 
				
			||||||
        <div className="flex items-center space-x-4">
 | 
					        <div className="flex items-center space-x-4">
 | 
				
			||||||
          <span className="text-sm font-semibold text-slate-700">並び替え:</span>
 | 
					          <span className="text-sm font-semibold text-slate-700">
 | 
				
			||||||
 | 
					            並び替え:
 | 
				
			||||||
 | 
					          </span>
 | 
				
			||||||
          <div className="flex space-x-2">
 | 
					          <div className="flex space-x-2">
 | 
				
			||||||
            <button
 | 
					            <button
 | 
				
			||||||
              onClick={() => handleSort('date')}
 | 
					              onClick={() => handleSort("date")}
 | 
				
			||||||
              className={`flex items-center space-x-2 px-3 py-1.5 rounded-lg text-sm font-medium transition-all duration-200 ${
 | 
					              className={`flex items-center space-x-2 px-3 py-1.5 rounded-lg text-sm font-medium transition-all duration-200 ${
 | 
				
			||||||
                sortBy === 'date'
 | 
					                sortBy === "date"
 | 
				
			||||||
                  ? 'bg-blue-100 text-blue-800 border border-blue-200'
 | 
					                  ? "bg-blue-100 text-blue-800 border border-blue-200"
 | 
				
			||||||
                  : 'text-slate-600 hover:text-slate-800 hover:bg-slate-100'
 | 
					                  : "text-slate-600 hover:text-slate-800 hover:bg-slate-100"
 | 
				
			||||||
              }`}
 | 
					              }`}
 | 
				
			||||||
            >
 | 
					            >
 | 
				
			||||||
              <span>日時</span>
 | 
					              <span>日時</span>
 | 
				
			||||||
              {sortBy === 'date' && (
 | 
					              {sortBy === "date" && (
 | 
				
			||||||
                <span>
 | 
					                <span>{sortOrder === "asc" ? "↑" : "↓"}</span>
 | 
				
			||||||
                  {sortOrder === 'asc' ? '↑' : '↓'}
 | 
					 | 
				
			||||||
                </span>
 | 
					 | 
				
			||||||
              )}
 | 
					              )}
 | 
				
			||||||
            </button>
 | 
					            </button>
 | 
				
			||||||
            <button
 | 
					            <button
 | 
				
			||||||
              onClick={() => handleSort('title')}
 | 
					              onClick={() => handleSort("title")}
 | 
				
			||||||
              className={`flex items-center space-x-2 px-3 py-1.5 rounded-lg text-sm font-medium transition-all duration-200 ${
 | 
					              className={`flex items-center space-x-2 px-3 py-1.5 rounded-lg text-sm font-medium transition-all duration-200 ${
 | 
				
			||||||
                sortBy === 'title'
 | 
					                sortBy === "title"
 | 
				
			||||||
                  ? 'bg-blue-100 text-blue-800 border border-blue-200'
 | 
					                  ? "bg-blue-100 text-blue-800 border border-blue-200"
 | 
				
			||||||
                  : 'text-slate-600 hover:text-slate-800 hover:bg-slate-100'
 | 
					                  : "text-slate-600 hover:text-slate-800 hover:bg-slate-100"
 | 
				
			||||||
              }`}
 | 
					              }`}
 | 
				
			||||||
            >
 | 
					            >
 | 
				
			||||||
              <span>タイトル</span>
 | 
					              <span>タイトル</span>
 | 
				
			||||||
              {sortBy === 'title' && (
 | 
					              {sortBy === "title" && (
 | 
				
			||||||
                <span>
 | 
					                <span>{sortOrder === "asc" ? "↑" : "↓"}</span>
 | 
				
			||||||
                  {sortOrder === 'asc' ? '↑' : '↓'}
 | 
					 | 
				
			||||||
                </span>
 | 
					 | 
				
			||||||
              )}
 | 
					              )}
 | 
				
			||||||
            </button>
 | 
					            </button>
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
@@ -179,17 +188,23 @@ export default function FeedList({ searchTerm = "", categoryFilter = "" }: FeedL
 | 
				
			|||||||
      {/* Feed Cards */}
 | 
					      {/* Feed Cards */}
 | 
				
			||||||
      {filteredAndSortedFeeds.length === 0 ? (
 | 
					      {filteredAndSortedFeeds.length === 0 ? (
 | 
				
			||||||
        <div className="text-center py-20">
 | 
					        <div className="text-center py-20">
 | 
				
			||||||
          <div className="w-24 h-24 rounded-3xl flex items-center justify-center mx-auto mb-6 shadow-lg" style={{background: 'linear-gradient(135deg, #e2e8f0, #cbd5e1)'}}>
 | 
					          <div
 | 
				
			||||||
            <span role="img" aria-hidden="true" className="text-4xl">📰</span>
 | 
					            className="w-24 h-24 rounded-3xl flex items-center justify-center mx-auto mb-6 shadow-lg"
 | 
				
			||||||
 | 
					            style={{ background: "linear-gradient(135deg, #e2e8f0, #cbd5e1)" }}
 | 
				
			||||||
 | 
					          >
 | 
				
			||||||
 | 
					            <span role="img" aria-hidden="true" className="text-4xl">
 | 
				
			||||||
 | 
					              📰
 | 
				
			||||||
 | 
					            </span>
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
          <h3 className="text-xl font-bold text-slate-700 mb-2">
 | 
					          <h3 className="text-xl font-bold text-slate-700 mb-2">
 | 
				
			||||||
            {searchTerm || categoryFilter ? '検索結果がありません' : 'フィードがありません'}
 | 
					            {searchTerm || categoryFilter
 | 
				
			||||||
 | 
					              ? "検索結果がありません"
 | 
				
			||||||
 | 
					              : "フィードがありません"}
 | 
				
			||||||
          </h3>
 | 
					          </h3>
 | 
				
			||||||
          <p className="text-slate-500 max-w-md mx-auto">
 | 
					          <p className="text-slate-500 max-w-md mx-auto">
 | 
				
			||||||
            {searchTerm || categoryFilter
 | 
					            {searchTerm || categoryFilter
 | 
				
			||||||
              ? '別のキーワードやカテゴリで検索してみてください' 
 | 
					              ? "別のキーワードやカテゴリで検索してみてください"
 | 
				
			||||||
              : 'RSSフィードを追加してバッチ処理を実行してください'
 | 
					              : "RSSフィードを追加してバッチ処理を実行してください"}
 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
          </p>
 | 
					          </p>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
      ) : (
 | 
					      ) : (
 | 
				
			||||||
@@ -199,15 +214,20 @@ export default function FeedList({ searchTerm = "", categoryFilter = "" }: FeedL
 | 
				
			|||||||
              key={feed.id}
 | 
					              key={feed.id}
 | 
				
			||||||
              className="group glass-effect rounded-3xl border border-white/20 hover:border-white/40 hover:shadow-2xl transition-all duration-300 overflow-hidden"
 | 
					              className="group glass-effect rounded-3xl border border-white/20 hover:border-white/40 hover:shadow-2xl transition-all duration-300 overflow-hidden"
 | 
				
			||||||
              style={{
 | 
					              style={{
 | 
				
			||||||
                animationDelay: `${index * 0.05}s`
 | 
					                animationDelay: `${index * 0.05}s`,
 | 
				
			||||||
              }}
 | 
					              }}
 | 
				
			||||||
            >
 | 
					            >
 | 
				
			||||||
              <div className="p-6">
 | 
					              <div className="p-6">
 | 
				
			||||||
                <div className="flex items-start space-x-5">
 | 
					                <div className="flex items-start space-x-5">
 | 
				
			||||||
                  {/* Article Icon */}
 | 
					                  {/* Article Icon */}
 | 
				
			||||||
                  <div className="flex-shrink-0">
 | 
					                  <div className="flex-shrink-0">
 | 
				
			||||||
                    <div className="w-16 h-16 rounded-2xl flex items-center justify-center shadow-lg group-hover:shadow-xl transition-all duration-300 group-hover:scale-110" style={{background: 'var(--gradient-primary)'}}>
 | 
					                    <div
 | 
				
			||||||
                      <span role="img" aria-hidden="true" className="text-2xl">📰</span>
 | 
					                      className="w-16 h-16 rounded-2xl flex items-center justify-center shadow-lg group-hover:shadow-xl transition-all duration-300 group-hover:scale-110"
 | 
				
			||||||
 | 
					                      style={{ background: "var(--gradient-primary)" }}
 | 
				
			||||||
 | 
					                    >
 | 
				
			||||||
 | 
					                      <span role="img" aria-hidden="true" className="text-2xl">
 | 
				
			||||||
 | 
					                        📰
 | 
				
			||||||
 | 
					                      </span>
 | 
				
			||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
                  </div>
 | 
					                  </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -223,7 +243,9 @@ export default function FeedList({ searchTerm = "", categoryFilter = "" }: FeedL
 | 
				
			|||||||
                        {/* Meta Info */}
 | 
					                        {/* Meta Info */}
 | 
				
			||||||
                        <div className="flex items-center space-x-3 mt-2 text-sm text-slate-600">
 | 
					                        <div className="flex items-center space-x-3 mt-2 text-sm text-slate-600">
 | 
				
			||||||
                          {feed.source?.title && (
 | 
					                          {feed.source?.title && (
 | 
				
			||||||
                            <span className="font-medium">{feed.source.title}</span>
 | 
					                            <span className="font-medium">
 | 
				
			||||||
 | 
					                              {feed.source.title}
 | 
				
			||||||
 | 
					                            </span>
 | 
				
			||||||
                          )}
 | 
					                          )}
 | 
				
			||||||
                          <span className="text-slate-400">•</span>
 | 
					                          <span className="text-slate-400">•</span>
 | 
				
			||||||
                          <span>{formatDate(feed.pubDate)}</span>
 | 
					                          <span>{formatDate(feed.pubDate)}</span>
 | 
				
			||||||
@@ -256,9 +278,7 @@ export default function FeedList({ searchTerm = "", categoryFilter = "" }: FeedL
 | 
				
			|||||||
                        元記事を読む →
 | 
					                        元記事を読む →
 | 
				
			||||||
                      </a>
 | 
					                      </a>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                      <div className="text-xs text-slate-400">
 | 
					                      <div className="text-xs text-slate-400">#{index + 1}</div>
 | 
				
			||||||
                        #{index + 1}
 | 
					 | 
				
			||||||
                      </div>
 | 
					 | 
				
			||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
                  </div>
 | 
					                  </div>
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,5 @@
 | 
				
			|||||||
import { useEffect, useState } from "react";
 | 
					import { useEffect, useState } from "react";
 | 
				
			||||||
 | 
					import React from "react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface Feed {
 | 
					interface Feed {
 | 
				
			||||||
  id: string;
 | 
					  id: string;
 | 
				
			||||||
@@ -76,6 +77,7 @@ export default function FeedManager() {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
    } catch (err) {
 | 
					    } catch (err) {
 | 
				
			||||||
      alert("エラーが発生しました");
 | 
					      alert("エラーが発生しました");
 | 
				
			||||||
 | 
					      console.error("Feed addition error:", err);
 | 
				
			||||||
    } finally {
 | 
					    } finally {
 | 
				
			||||||
      setAddingFeed(false);
 | 
					      setAddingFeed(false);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,9 +1,10 @@
 | 
				
			|||||||
import { StrictMode } from 'react'
 | 
					import { StrictMode } from "react";
 | 
				
			||||||
import { createRoot } from 'react-dom/client'
 | 
					import { createRoot } from "react-dom/client";
 | 
				
			||||||
import App from './App.tsx'
 | 
					import App from "./App.tsx";
 | 
				
			||||||
 | 
					import React from "react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
createRoot(document.getElementById('root')!).render(
 | 
					createRoot(document.getElementById("root")!).render(
 | 
				
			||||||
  <StrictMode>
 | 
					  <StrictMode>
 | 
				
			||||||
    <App />
 | 
					    <App />
 | 
				
			||||||
  </StrictMode>,
 | 
					  </StrictMode>,
 | 
				
			||||||
)
 | 
					);
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user