Some fixes
This commit is contained in:
		@@ -102,11 +102,26 @@ function App() {
 | 
			
		||||
  const [approvalNotes, setApprovalNotes] = useState<{ [key: string]: string }>(
 | 
			
		||||
    {},
 | 
			
		||||
  );
 | 
			
		||||
  const [editingSettings, setEditingSettings] = useState<{ [key: string]: string }>({});
 | 
			
		||||
  const [categories, setCategories] = useState<CategoryData>({ feedCategories: [], episodeCategories: [], allCategories: [] });
 | 
			
		||||
  const [categoryCounts, setCategoryCounts] = useState<{ [category: string]: CategoryCounts }>({});
 | 
			
		||||
  const [editingSettings, setEditingSettings] = useState<{
 | 
			
		||||
    [key: string]: string;
 | 
			
		||||
  }>({});
 | 
			
		||||
  const [categories, setCategories] = useState<CategoryData>({
 | 
			
		||||
    feedCategories: [],
 | 
			
		||||
    episodeCategories: [],
 | 
			
		||||
    allCategories: [],
 | 
			
		||||
  });
 | 
			
		||||
  const [categoryCounts, setCategoryCounts] = useState<{
 | 
			
		||||
    [category: string]: CategoryCounts;
 | 
			
		||||
  }>({});
 | 
			
		||||
  const [activeTab, setActiveTab] = useState<
 | 
			
		||||
    "dashboard" | "feeds" | "episodes" | "env" | "settings" | "batch" | "requests" | "categories"
 | 
			
		||||
    | "dashboard"
 | 
			
		||||
    | "feeds"
 | 
			
		||||
    | "episodes"
 | 
			
		||||
    | "env"
 | 
			
		||||
    | "settings"
 | 
			
		||||
    | "batch"
 | 
			
		||||
    | "requests"
 | 
			
		||||
    | "categories"
 | 
			
		||||
  >("dashboard");
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
@@ -116,16 +131,23 @@ function App() {
 | 
			
		||||
  const loadData = async () => {
 | 
			
		||||
    setLoading(true);
 | 
			
		||||
    try {
 | 
			
		||||
      const [feedsRes, statsRes, envRes, settingsRes, requestsRes, episodesRes, categoriesRes] =
 | 
			
		||||
        await Promise.all([
 | 
			
		||||
          fetch("/api/admin/feeds"),
 | 
			
		||||
          fetch("/api/admin/stats"),
 | 
			
		||||
          fetch("/api/admin/env"),
 | 
			
		||||
          fetch("/api/admin/settings"),
 | 
			
		||||
          fetch("/api/admin/feed-requests"),
 | 
			
		||||
          fetch("/api/admin/episodes"),
 | 
			
		||||
          fetch("/api/admin/categories/all"),
 | 
			
		||||
        ]);
 | 
			
		||||
      const [
 | 
			
		||||
        feedsRes,
 | 
			
		||||
        statsRes,
 | 
			
		||||
        envRes,
 | 
			
		||||
        settingsRes,
 | 
			
		||||
        requestsRes,
 | 
			
		||||
        episodesRes,
 | 
			
		||||
        categoriesRes,
 | 
			
		||||
      ] = await Promise.all([
 | 
			
		||||
        fetch("/api/admin/feeds"),
 | 
			
		||||
        fetch("/api/admin/stats"),
 | 
			
		||||
        fetch("/api/admin/env"),
 | 
			
		||||
        fetch("/api/admin/settings"),
 | 
			
		||||
        fetch("/api/admin/feed-requests"),
 | 
			
		||||
        fetch("/api/admin/episodes"),
 | 
			
		||||
        fetch("/api/admin/categories/all"),
 | 
			
		||||
      ]);
 | 
			
		||||
 | 
			
		||||
      if (
 | 
			
		||||
        !feedsRes.ok ||
 | 
			
		||||
@@ -139,16 +161,23 @@ function App() {
 | 
			
		||||
        throw new Error("Failed to load data");
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      const [feedsData, statsData, envData, settingsData, requestsData, episodesData, categoriesData] =
 | 
			
		||||
        await Promise.all([
 | 
			
		||||
          feedsRes.json(),
 | 
			
		||||
          statsRes.json(),
 | 
			
		||||
          envRes.json(),
 | 
			
		||||
          settingsRes.json(),
 | 
			
		||||
          requestsRes.json(),
 | 
			
		||||
          episodesRes.json(),
 | 
			
		||||
          categoriesRes.json(),
 | 
			
		||||
        ]);
 | 
			
		||||
      const [
 | 
			
		||||
        feedsData,
 | 
			
		||||
        statsData,
 | 
			
		||||
        envData,
 | 
			
		||||
        settingsData,
 | 
			
		||||
        requestsData,
 | 
			
		||||
        episodesData,
 | 
			
		||||
        categoriesData,
 | 
			
		||||
      ] = await Promise.all([
 | 
			
		||||
        feedsRes.json(),
 | 
			
		||||
        statsRes.json(),
 | 
			
		||||
        envRes.json(),
 | 
			
		||||
        settingsRes.json(),
 | 
			
		||||
        requestsRes.json(),
 | 
			
		||||
        episodesRes.json(),
 | 
			
		||||
        categoriesRes.json(),
 | 
			
		||||
      ]);
 | 
			
		||||
 | 
			
		||||
      setFeeds(feedsData);
 | 
			
		||||
      setStats(statsData);
 | 
			
		||||
@@ -157,24 +186,28 @@ function App() {
 | 
			
		||||
      setFeedRequests(requestsData);
 | 
			
		||||
      setEpisodes(episodesData);
 | 
			
		||||
      setCategories(categoriesData);
 | 
			
		||||
      
 | 
			
		||||
 | 
			
		||||
      // Load category counts for all categories
 | 
			
		||||
      const countsPromises = categoriesData.allCategories.map(async (category: string) => {
 | 
			
		||||
        const res = await fetch(`/api/admin/categories/${encodeURIComponent(category)}/counts`);
 | 
			
		||||
        if (res.ok) {
 | 
			
		||||
          const counts = await res.json();
 | 
			
		||||
          return { category, counts };
 | 
			
		||||
        }
 | 
			
		||||
        return { category, counts: { feedCount: 0, episodeCount: 0 } };
 | 
			
		||||
      });
 | 
			
		||||
      
 | 
			
		||||
      const countsPromises = categoriesData.allCategories.map(
 | 
			
		||||
        async (category: string) => {
 | 
			
		||||
          const res = await fetch(
 | 
			
		||||
            `/api/admin/categories/${encodeURIComponent(category)}/counts`,
 | 
			
		||||
          );
 | 
			
		||||
          if (res.ok) {
 | 
			
		||||
            const counts = await res.json();
 | 
			
		||||
            return { category, counts };
 | 
			
		||||
          }
 | 
			
		||||
          return { category, counts: { feedCount: 0, episodeCount: 0 } };
 | 
			
		||||
        },
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const countsResults = await Promise.all(countsPromises);
 | 
			
		||||
      const countsMap: { [category: string]: CategoryCounts } = {};
 | 
			
		||||
      countsResults.forEach(({ category, counts }) => {
 | 
			
		||||
        countsMap[category] = counts;
 | 
			
		||||
      });
 | 
			
		||||
      setCategoryCounts(countsMap);
 | 
			
		||||
      
 | 
			
		||||
 | 
			
		||||
      setError(null);
 | 
			
		||||
    } catch (err) {
 | 
			
		||||
      setError("データの読み込みに失敗しました");
 | 
			
		||||
@@ -426,26 +459,44 @@ function App() {
 | 
			
		||||
    setEditingSettings({ ...editingSettings, [key]: value });
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const deleteCategory = async (category: string, target: "feeds" | "episodes" | "both") => {
 | 
			
		||||
    const targetText = target === "both" ? "フィードとエピソード" : target === "feeds" ? "フィード" : "エピソード";
 | 
			
		||||
    const counts = categoryCounts[category] || { feedCount: 0, episodeCount: 0 };
 | 
			
		||||
    const totalCount = target === "both" ? counts.feedCount + counts.episodeCount : 
 | 
			
		||||
                      target === "feeds" ? counts.feedCount : counts.episodeCount;
 | 
			
		||||
  const deleteCategory = async (
 | 
			
		||||
    category: string,
 | 
			
		||||
    target: "feeds" | "episodes" | "both",
 | 
			
		||||
  ) => {
 | 
			
		||||
    const targetText =
 | 
			
		||||
      target === "both"
 | 
			
		||||
        ? "フィードとエピソード"
 | 
			
		||||
        : target === "feeds"
 | 
			
		||||
          ? "フィード"
 | 
			
		||||
          : "エピソード";
 | 
			
		||||
    const counts = categoryCounts[category] || {
 | 
			
		||||
      feedCount: 0,
 | 
			
		||||
      episodeCount: 0,
 | 
			
		||||
    };
 | 
			
		||||
    const totalCount =
 | 
			
		||||
      target === "both"
 | 
			
		||||
        ? counts.feedCount + counts.episodeCount
 | 
			
		||||
        : target === "feeds"
 | 
			
		||||
          ? counts.feedCount
 | 
			
		||||
          : counts.episodeCount;
 | 
			
		||||
 | 
			
		||||
    if (
 | 
			
		||||
      !confirm(
 | 
			
		||||
        `本当にカテゴリ「${category}」を${targetText}から削除しますか?\n\n${totalCount}件のアイテムが影響を受けます。この操作は取り消せません。`
 | 
			
		||||
        `本当にカテゴリ「${category}」を${targetText}から削除しますか?\n\n${totalCount}件のアイテムが影響を受けます。この操作は取り消せません。`,
 | 
			
		||||
      )
 | 
			
		||||
    ) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
      const res = await fetch(`/api/admin/categories/${encodeURIComponent(category)}`, {
 | 
			
		||||
        method: "DELETE",
 | 
			
		||||
        headers: { "Content-Type": "application/json" },
 | 
			
		||||
        body: JSON.stringify({ target }),
 | 
			
		||||
      });
 | 
			
		||||
      const res = await fetch(
 | 
			
		||||
        `/api/admin/categories/${encodeURIComponent(category)}`,
 | 
			
		||||
        {
 | 
			
		||||
          method: "DELETE",
 | 
			
		||||
          headers: { "Content-Type": "application/json" },
 | 
			
		||||
          body: JSON.stringify({ target }),
 | 
			
		||||
        },
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const data = await res.json();
 | 
			
		||||
 | 
			
		||||
@@ -1230,26 +1281,33 @@ function App() {
 | 
			
		||||
              </p>
 | 
			
		||||
 | 
			
		||||
              <div style={{ marginBottom: "20px" }}>
 | 
			
		||||
                <div className="stats-grid" style={{ gridTemplateColumns: "repeat(4, 1fr)" }}>
 | 
			
		||||
                <div
 | 
			
		||||
                  className="stats-grid"
 | 
			
		||||
                  style={{ gridTemplateColumns: "repeat(4, 1fr)" }}
 | 
			
		||||
                >
 | 
			
		||||
                  <div className="stat-card">
 | 
			
		||||
                    <div className="value">{settings.length}</div>
 | 
			
		||||
                    <div className="label">総設定数</div>
 | 
			
		||||
                  </div>
 | 
			
		||||
                  <div className="stat-card">
 | 
			
		||||
                    <div className="value">
 | 
			
		||||
                      {settings.filter(s => s.value !== null && s.value !== "").length}
 | 
			
		||||
                      {
 | 
			
		||||
                        settings.filter(
 | 
			
		||||
                          (s) => s.value !== null && s.value !== "",
 | 
			
		||||
                        ).length
 | 
			
		||||
                      }
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div className="label">設定済み</div>
 | 
			
		||||
                  </div>
 | 
			
		||||
                  <div className="stat-card">
 | 
			
		||||
                    <div className="value">
 | 
			
		||||
                      {settings.filter(s => s.required).length}
 | 
			
		||||
                      {settings.filter((s) => s.required).length}
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div className="label">必須設定</div>
 | 
			
		||||
                  </div>
 | 
			
		||||
                  <div className="stat-card">
 | 
			
		||||
                    <div className="value">
 | 
			
		||||
                      {settings.filter(s => s.isCredential).length}
 | 
			
		||||
                      {settings.filter((s) => s.isCredential).length}
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div className="label">認証情報</div>
 | 
			
		||||
                  </div>
 | 
			
		||||
@@ -1258,63 +1316,109 @@ function App() {
 | 
			
		||||
 | 
			
		||||
              <div className="settings-list">
 | 
			
		||||
                {settings.map((setting) => (
 | 
			
		||||
                  <div key={setting.key} className="setting-item" style={{ 
 | 
			
		||||
                    display: "flex", 
 | 
			
		||||
                    alignItems: "center",
 | 
			
		||||
                    padding: "16px",
 | 
			
		||||
                    border: "1px solid #ddd",
 | 
			
		||||
                    borderRadius: "4px",
 | 
			
		||||
                    marginBottom: "12px",
 | 
			
		||||
                    backgroundColor: setting.required && (!setting.value || setting.value === "") ? "#fff5f5" : "#fff"
 | 
			
		||||
                  }}>
 | 
			
		||||
                  <div
 | 
			
		||||
                    key={setting.key}
 | 
			
		||||
                    className="setting-item"
 | 
			
		||||
                    style={{
 | 
			
		||||
                      display: "flex",
 | 
			
		||||
                      alignItems: "center",
 | 
			
		||||
                      padding: "16px",
 | 
			
		||||
                      border: "1px solid #ddd",
 | 
			
		||||
                      borderRadius: "4px",
 | 
			
		||||
                      marginBottom: "12px",
 | 
			
		||||
                      backgroundColor:
 | 
			
		||||
                        setting.required &&
 | 
			
		||||
                        (!setting.value || setting.value === "")
 | 
			
		||||
                          ? "#fff5f5"
 | 
			
		||||
                          : "#fff",
 | 
			
		||||
                    }}
 | 
			
		||||
                  >
 | 
			
		||||
                    <div style={{ flex: 1 }}>
 | 
			
		||||
                      <div style={{ display: "flex", alignItems: "center", gap: "8px", marginBottom: "4px" }}>
 | 
			
		||||
                        <h4 style={{ margin: 0, fontSize: "16px" }}>{setting.key}</h4>
 | 
			
		||||
                      <div
 | 
			
		||||
                        style={{
 | 
			
		||||
                          display: "flex",
 | 
			
		||||
                          alignItems: "center",
 | 
			
		||||
                          gap: "8px",
 | 
			
		||||
                          marginBottom: "4px",
 | 
			
		||||
                        }}
 | 
			
		||||
                      >
 | 
			
		||||
                        <h4 style={{ margin: 0, fontSize: "16px" }}>
 | 
			
		||||
                          {setting.key}
 | 
			
		||||
                        </h4>
 | 
			
		||||
                        {setting.required && (
 | 
			
		||||
                          <span style={{
 | 
			
		||||
                            padding: "2px 6px",
 | 
			
		||||
                            background: "#dc3545",
 | 
			
		||||
                            color: "white",
 | 
			
		||||
                            borderRadius: "4px",
 | 
			
		||||
                            fontSize: "10px"
 | 
			
		||||
                          }}>必須</span>
 | 
			
		||||
                          <span
 | 
			
		||||
                            style={{
 | 
			
		||||
                              padding: "2px 6px",
 | 
			
		||||
                              background: "#dc3545",
 | 
			
		||||
                              color: "white",
 | 
			
		||||
                              borderRadius: "4px",
 | 
			
		||||
                              fontSize: "10px",
 | 
			
		||||
                            }}
 | 
			
		||||
                          >
 | 
			
		||||
                            必須
 | 
			
		||||
                          </span>
 | 
			
		||||
                        )}
 | 
			
		||||
                        {setting.isCredential && (
 | 
			
		||||
                          <span style={{
 | 
			
		||||
                            padding: "2px 6px",
 | 
			
		||||
                            background: "#ffc107",
 | 
			
		||||
                            color: "black",
 | 
			
		||||
                            borderRadius: "4px",
 | 
			
		||||
                            fontSize: "10px"
 | 
			
		||||
                          }}>認証情報</span>
 | 
			
		||||
                          <span
 | 
			
		||||
                            style={{
 | 
			
		||||
                              padding: "2px 6px",
 | 
			
		||||
                              background: "#ffc107",
 | 
			
		||||
                              color: "black",
 | 
			
		||||
                              borderRadius: "4px",
 | 
			
		||||
                              fontSize: "10px",
 | 
			
		||||
                            }}
 | 
			
		||||
                          >
 | 
			
		||||
                            認証情報
 | 
			
		||||
                          </span>
 | 
			
		||||
                        )}
 | 
			
		||||
                      </div>
 | 
			
		||||
                      <p style={{ margin: "0 0 8px 0", fontSize: "14px", color: "#666" }}>
 | 
			
		||||
                      <p
 | 
			
		||||
                        style={{
 | 
			
		||||
                          margin: "0 0 8px 0",
 | 
			
		||||
                          fontSize: "14px",
 | 
			
		||||
                          color: "#666",
 | 
			
		||||
                        }}
 | 
			
		||||
                      >
 | 
			
		||||
                        {setting.description}
 | 
			
		||||
                      </p>
 | 
			
		||||
                      <div style={{ fontSize: "12px", color: "#999" }}>
 | 
			
		||||
                        デフォルト値: {setting.defaultValue || "なし"} | 
 | 
			
		||||
                        最終更新: {new Date(setting.updatedAt).toLocaleString("ja-JP")}
 | 
			
		||||
                        デフォルト値: {setting.defaultValue || "なし"} |
 | 
			
		||||
                        最終更新:{" "}
 | 
			
		||||
                        {new Date(setting.updatedAt).toLocaleString("ja-JP")}
 | 
			
		||||
                      </div>
 | 
			
		||||
                      
 | 
			
		||||
 | 
			
		||||
                      {editingSettings[setting.key] !== undefined ? (
 | 
			
		||||
                        <div style={{ marginTop: "8px", display: "flex", gap: "8px", alignItems: "center" }}>
 | 
			
		||||
                        <div
 | 
			
		||||
                          style={{
 | 
			
		||||
                            marginTop: "8px",
 | 
			
		||||
                            display: "flex",
 | 
			
		||||
                            gap: "8px",
 | 
			
		||||
                            alignItems: "center",
 | 
			
		||||
                          }}
 | 
			
		||||
                        >
 | 
			
		||||
                          <input
 | 
			
		||||
                            type={setting.isCredential ? "password" : "text"}
 | 
			
		||||
                            value={editingSettings[setting.key]}
 | 
			
		||||
                            onChange={(e) => updateEditingValue(setting.key, e.target.value)}
 | 
			
		||||
                            onChange={(e) =>
 | 
			
		||||
                              updateEditingValue(setting.key, e.target.value)
 | 
			
		||||
                            }
 | 
			
		||||
                            placeholder={setting.defaultValue || "値を入力..."}
 | 
			
		||||
                            style={{
 | 
			
		||||
                              flex: 1,
 | 
			
		||||
                              padding: "6px 10px",
 | 
			
		||||
                              border: "1px solid #ddd",
 | 
			
		||||
                              borderRadius: "4px",
 | 
			
		||||
                              fontSize: "14px"
 | 
			
		||||
                              fontSize: "14px",
 | 
			
		||||
                            }}
 | 
			
		||||
                          />
 | 
			
		||||
                          <button
 | 
			
		||||
                            className="btn btn-success"
 | 
			
		||||
                            onClick={() => updateSetting(setting.key, editingSettings[setting.key])}
 | 
			
		||||
                            onClick={() =>
 | 
			
		||||
                              updateSetting(
 | 
			
		||||
                                setting.key,
 | 
			
		||||
                                editingSettings[setting.key],
 | 
			
		||||
                              )
 | 
			
		||||
                            }
 | 
			
		||||
                            style={{ fontSize: "12px", padding: "6px 12px" }}
 | 
			
		||||
                          >
 | 
			
		||||
                            保存
 | 
			
		||||
@@ -1328,20 +1432,36 @@ function App() {
 | 
			
		||||
                          </button>
 | 
			
		||||
                        </div>
 | 
			
		||||
                      ) : (
 | 
			
		||||
                        <div style={{ marginTop: "8px", display: "flex", alignItems: "center", gap: "12px" }}>
 | 
			
		||||
                          <div style={{
 | 
			
		||||
                            flex: 1,
 | 
			
		||||
                            padding: "6px 10px",
 | 
			
		||||
                            background: "#f8f9fa",
 | 
			
		||||
                            border: "1px solid #e9ecef",
 | 
			
		||||
                            borderRadius: "4px",
 | 
			
		||||
                            fontSize: "14px",
 | 
			
		||||
                            minHeight: "32px",
 | 
			
		||||
                        <div
 | 
			
		||||
                          style={{
 | 
			
		||||
                            marginTop: "8px",
 | 
			
		||||
                            display: "flex",
 | 
			
		||||
                            alignItems: "center"
 | 
			
		||||
                          }}>
 | 
			
		||||
                            alignItems: "center",
 | 
			
		||||
                            gap: "12px",
 | 
			
		||||
                          }}
 | 
			
		||||
                        >
 | 
			
		||||
                          <div
 | 
			
		||||
                            style={{
 | 
			
		||||
                              flex: 1,
 | 
			
		||||
                              padding: "6px 10px",
 | 
			
		||||
                              background: "#f8f9fa",
 | 
			
		||||
                              border: "1px solid #e9ecef",
 | 
			
		||||
                              borderRadius: "4px",
 | 
			
		||||
                              fontSize: "14px",
 | 
			
		||||
                              minHeight: "32px",
 | 
			
		||||
                              display: "flex",
 | 
			
		||||
                              alignItems: "center",
 | 
			
		||||
                            }}
 | 
			
		||||
                          >
 | 
			
		||||
                            {setting.value === null || setting.value === "" ? (
 | 
			
		||||
                              <span style={{ color: "#dc3545", fontStyle: "italic" }}>未設定</span>
 | 
			
		||||
                              <span
 | 
			
		||||
                                style={{
 | 
			
		||||
                                  color: "#dc3545",
 | 
			
		||||
                                  fontStyle: "italic",
 | 
			
		||||
                                }}
 | 
			
		||||
                              >
 | 
			
		||||
                                未設定
 | 
			
		||||
                              </span>
 | 
			
		||||
                            ) : setting.isCredential ? (
 | 
			
		||||
                              <span style={{ color: "#666" }}>••••••••</span>
 | 
			
		||||
                            ) : (
 | 
			
		||||
@@ -1350,7 +1470,9 @@ function App() {
 | 
			
		||||
                          </div>
 | 
			
		||||
                          <button
 | 
			
		||||
                            className="btn btn-primary"
 | 
			
		||||
                            onClick={() => startEditingSetting(setting.key, setting.value)}
 | 
			
		||||
                            onClick={() =>
 | 
			
		||||
                              startEditingSetting(setting.key, setting.value)
 | 
			
		||||
                            }
 | 
			
		||||
                            style={{ fontSize: "12px", padding: "6px 12px" }}
 | 
			
		||||
                          >
 | 
			
		||||
                            編集
 | 
			
		||||
@@ -1379,10 +1501,18 @@ function App() {
 | 
			
		||||
                    paddingLeft: "20px",
 | 
			
		||||
                  }}
 | 
			
		||||
                >
 | 
			
		||||
                  <li><strong>必須設定:</strong> アプリケーションの動作に必要な設定です</li>
 | 
			
		||||
                  <li><strong>認証情報:</strong> セキュリティ上重要な情報で、表示時にマスクされます</li>
 | 
			
		||||
                  <li>
 | 
			
		||||
                    <strong>必須設定:</strong>{" "}
 | 
			
		||||
                    アプリケーションの動作に必要な設定です
 | 
			
		||||
                  </li>
 | 
			
		||||
                  <li>
 | 
			
		||||
                    <strong>認証情報:</strong>{" "}
 | 
			
		||||
                    セキュリティ上重要な情報で、表示時にマスクされます
 | 
			
		||||
                  </li>
 | 
			
		||||
                  <li>設定の変更は即座にアプリケーションに反映されます</li>
 | 
			
		||||
                  <li>デフォルト値が設定されている項目は、空にするとデフォルト値が使用されます</li>
 | 
			
		||||
                  <li>
 | 
			
		||||
                    デフォルト値が設定されている項目は、空にするとデフォルト値が使用されます
 | 
			
		||||
                  </li>
 | 
			
		||||
                </ul>
 | 
			
		||||
              </div>
 | 
			
		||||
            </>
 | 
			
		||||
@@ -1396,17 +1526,26 @@ function App() {
 | 
			
		||||
              </p>
 | 
			
		||||
 | 
			
		||||
              <div style={{ marginBottom: "20px" }}>
 | 
			
		||||
                <div className="stats-grid" style={{ gridTemplateColumns: "repeat(3, 1fr)" }}>
 | 
			
		||||
                <div
 | 
			
		||||
                  className="stats-grid"
 | 
			
		||||
                  style={{ gridTemplateColumns: "repeat(3, 1fr)" }}
 | 
			
		||||
                >
 | 
			
		||||
                  <div className="stat-card">
 | 
			
		||||
                    <div className="value">{categories.allCategories.length}</div>
 | 
			
		||||
                    <div className="value">
 | 
			
		||||
                      {categories.allCategories.length}
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div className="label">総カテゴリ数</div>
 | 
			
		||||
                  </div>
 | 
			
		||||
                  <div className="stat-card">
 | 
			
		||||
                    <div className="value">{categories.feedCategories.length}</div>
 | 
			
		||||
                    <div className="value">
 | 
			
		||||
                      {categories.feedCategories.length}
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div className="label">フィードカテゴリ</div>
 | 
			
		||||
                  </div>
 | 
			
		||||
                  <div className="stat-card">
 | 
			
		||||
                    <div className="value">{categories.episodeCategories.length}</div>
 | 
			
		||||
                    <div className="value">
 | 
			
		||||
                      {categories.episodeCategories.length}
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div className="label">エピソードカテゴリ</div>
 | 
			
		||||
                  </div>
 | 
			
		||||
                </div>
 | 
			
		||||
@@ -1425,16 +1564,27 @@ function App() {
 | 
			
		||||
              ) : (
 | 
			
		||||
                <div style={{ marginTop: "24px" }}>
 | 
			
		||||
                  <h4>カテゴリ一覧 ({categories.allCategories.length}件)</h4>
 | 
			
		||||
                  <div style={{ marginBottom: "16px", fontSize: "14px", color: "#666" }}>
 | 
			
		||||
                  <div
 | 
			
		||||
                    style={{
 | 
			
		||||
                      marginBottom: "16px",
 | 
			
		||||
                      fontSize: "14px",
 | 
			
		||||
                      color: "#666",
 | 
			
		||||
                    }}
 | 
			
		||||
                  >
 | 
			
		||||
                    削除対象を選択してから削除ボタンをクリックしてください。
 | 
			
		||||
                  </div>
 | 
			
		||||
                  
 | 
			
		||||
 | 
			
		||||
                  <div className="category-list">
 | 
			
		||||
                    {categories.allCategories.map((category) => {
 | 
			
		||||
                      const counts = categoryCounts[category] || { feedCount: 0, episodeCount: 0 };
 | 
			
		||||
                      const isInFeeds = categories.feedCategories.includes(category);
 | 
			
		||||
                      const isInEpisodes = categories.episodeCategories.includes(category);
 | 
			
		||||
                      
 | 
			
		||||
                      const counts = categoryCounts[category] || {
 | 
			
		||||
                        feedCount: 0,
 | 
			
		||||
                        episodeCount: 0,
 | 
			
		||||
                      };
 | 
			
		||||
                      const isInFeeds =
 | 
			
		||||
                        categories.feedCategories.includes(category);
 | 
			
		||||
                      const isInEpisodes =
 | 
			
		||||
                        categories.episodeCategories.includes(category);
 | 
			
		||||
 | 
			
		||||
                      return (
 | 
			
		||||
                        <div
 | 
			
		||||
                          key={category}
 | 
			
		||||
@@ -1442,42 +1592,70 @@ function App() {
 | 
			
		||||
                          style={{ marginBottom: "16px" }}
 | 
			
		||||
                        >
 | 
			
		||||
                          <div className="feed-info">
 | 
			
		||||
                            <h4 style={{ margin: "0 0 8px 0", fontSize: "16px" }}>
 | 
			
		||||
                            <h4
 | 
			
		||||
                              style={{ margin: "0 0 8px 0", fontSize: "16px" }}
 | 
			
		||||
                            >
 | 
			
		||||
                              {category}
 | 
			
		||||
                            </h4>
 | 
			
		||||
                            <div style={{ fontSize: "14px", color: "#666", marginBottom: "8px" }}>
 | 
			
		||||
                            <div
 | 
			
		||||
                              style={{
 | 
			
		||||
                                fontSize: "14px",
 | 
			
		||||
                                color: "#666",
 | 
			
		||||
                                marginBottom: "8px",
 | 
			
		||||
                              }}
 | 
			
		||||
                            >
 | 
			
		||||
                              <span>フィード: {counts.feedCount}件</span>
 | 
			
		||||
                              <span style={{ margin: "0 12px" }}>|</span>
 | 
			
		||||
                              <span>エピソード: {counts.episodeCount}件</span>
 | 
			
		||||
                              <span style={{ margin: "0 12px" }}>|</span>
 | 
			
		||||
                              <span>合計: {counts.feedCount + counts.episodeCount}件</span>
 | 
			
		||||
                              <span>
 | 
			
		||||
                                合計: {counts.feedCount + counts.episodeCount}件
 | 
			
		||||
                              </span>
 | 
			
		||||
                            </div>
 | 
			
		||||
                            <div style={{ fontSize: "12px", color: "#999" }}>
 | 
			
		||||
                              <span style={{
 | 
			
		||||
                                padding: "2px 6px",
 | 
			
		||||
                                background: isInFeeds ? "#e3f2fd" : "#f5f5f5",
 | 
			
		||||
                                color: isInFeeds ? "#1976d2" : "#999",
 | 
			
		||||
                                borderRadius: "4px",
 | 
			
		||||
                                marginRight: "8px"
 | 
			
		||||
                              }}>
 | 
			
		||||
                              <span
 | 
			
		||||
                                style={{
 | 
			
		||||
                                  padding: "2px 6px",
 | 
			
		||||
                                  background: isInFeeds ? "#e3f2fd" : "#f5f5f5",
 | 
			
		||||
                                  color: isInFeeds ? "#1976d2" : "#999",
 | 
			
		||||
                                  borderRadius: "4px",
 | 
			
		||||
                                  marginRight: "8px",
 | 
			
		||||
                                }}
 | 
			
		||||
                              >
 | 
			
		||||
                                フィード: {isInFeeds ? "使用中" : "未使用"}
 | 
			
		||||
                              </span>
 | 
			
		||||
                              <span style={{
 | 
			
		||||
                                padding: "2px 6px",
 | 
			
		||||
                                background: isInEpisodes ? "#e8f5e8" : "#f5f5f5",
 | 
			
		||||
                                color: isInEpisodes ? "#388e3c" : "#999",
 | 
			
		||||
                                borderRadius: "4px"
 | 
			
		||||
                              }}>
 | 
			
		||||
                              <span
 | 
			
		||||
                                style={{
 | 
			
		||||
                                  padding: "2px 6px",
 | 
			
		||||
                                  background: isInEpisodes
 | 
			
		||||
                                    ? "#e8f5e8"
 | 
			
		||||
                                    : "#f5f5f5",
 | 
			
		||||
                                  color: isInEpisodes ? "#388e3c" : "#999",
 | 
			
		||||
                                  borderRadius: "4px",
 | 
			
		||||
                                }}
 | 
			
		||||
                              >
 | 
			
		||||
                                エピソード: {isInEpisodes ? "使用中" : "未使用"}
 | 
			
		||||
                              </span>
 | 
			
		||||
                            </div>
 | 
			
		||||
                          </div>
 | 
			
		||||
                          <div className="feed-actions" style={{ flexDirection: "column", gap: "8px", minWidth: "160px" }}>
 | 
			
		||||
                          <div
 | 
			
		||||
                            className="feed-actions"
 | 
			
		||||
                            style={{
 | 
			
		||||
                              flexDirection: "column",
 | 
			
		||||
                              gap: "8px",
 | 
			
		||||
                              minWidth: "160px",
 | 
			
		||||
                            }}
 | 
			
		||||
                          >
 | 
			
		||||
                            {isInFeeds && (
 | 
			
		||||
                              <button
 | 
			
		||||
                                className="btn btn-warning"
 | 
			
		||||
                                onClick={() => deleteCategory(category, "feeds")}
 | 
			
		||||
                                style={{ fontSize: "12px", padding: "6px 12px" }}
 | 
			
		||||
                                onClick={() =>
 | 
			
		||||
                                  deleteCategory(category, "feeds")
 | 
			
		||||
                                }
 | 
			
		||||
                                style={{
 | 
			
		||||
                                  fontSize: "12px",
 | 
			
		||||
                                  padding: "6px 12px",
 | 
			
		||||
                                }}
 | 
			
		||||
                              >
 | 
			
		||||
                                フィードから削除
 | 
			
		||||
                              </button>
 | 
			
		||||
@@ -1485,8 +1663,13 @@ function App() {
 | 
			
		||||
                            {isInEpisodes && (
 | 
			
		||||
                              <button
 | 
			
		||||
                                className="btn btn-warning"
 | 
			
		||||
                                onClick={() => deleteCategory(category, "episodes")}
 | 
			
		||||
                                style={{ fontSize: "12px", padding: "6px 12px" }}
 | 
			
		||||
                                onClick={() =>
 | 
			
		||||
                                  deleteCategory(category, "episodes")
 | 
			
		||||
                                }
 | 
			
		||||
                                style={{
 | 
			
		||||
                                  fontSize: "12px",
 | 
			
		||||
                                  padding: "6px 12px",
 | 
			
		||||
                                }}
 | 
			
		||||
                              >
 | 
			
		||||
                                エピソードから削除
 | 
			
		||||
                              </button>
 | 
			
		||||
@@ -1495,7 +1678,10 @@ function App() {
 | 
			
		||||
                              <button
 | 
			
		||||
                                className="btn btn-danger"
 | 
			
		||||
                                onClick={() => deleteCategory(category, "both")}
 | 
			
		||||
                                style={{ fontSize: "12px", padding: "6px 12px" }}
 | 
			
		||||
                                style={{
 | 
			
		||||
                                  fontSize: "12px",
 | 
			
		||||
                                  padding: "6px 12px",
 | 
			
		||||
                                }}
 | 
			
		||||
                              >
 | 
			
		||||
                                すべてから削除
 | 
			
		||||
                              </button>
 | 
			
		||||
@@ -1525,10 +1711,21 @@ function App() {
 | 
			
		||||
                    paddingLeft: "20px",
 | 
			
		||||
                  }}
 | 
			
		||||
                >
 | 
			
		||||
                  <li><strong>フィードから削除:</strong> フィードのカテゴリのみを削除します</li>
 | 
			
		||||
                  <li><strong>エピソードから削除:</strong> エピソードのカテゴリのみを削除します</li>
 | 
			
		||||
                  <li><strong>すべてから削除:</strong> フィードとエピソード両方からカテゴリを削除します</li>
 | 
			
		||||
                  <li>削除されたカテゴリは NULL に設定され、分類が解除されます</li>
 | 
			
		||||
                  <li>
 | 
			
		||||
                    <strong>フィードから削除:</strong>{" "}
 | 
			
		||||
                    フィードのカテゴリのみを削除します
 | 
			
		||||
                  </li>
 | 
			
		||||
                  <li>
 | 
			
		||||
                    <strong>エピソードから削除:</strong>{" "}
 | 
			
		||||
                    エピソードのカテゴリのみを削除します
 | 
			
		||||
                  </li>
 | 
			
		||||
                  <li>
 | 
			
		||||
                    <strong>すべてから削除:</strong>{" "}
 | 
			
		||||
                    フィードとエピソード両方からカテゴリを削除します
 | 
			
		||||
                  </li>
 | 
			
		||||
                  <li>
 | 
			
		||||
                    削除されたカテゴリは NULL に設定され、分類が解除されます
 | 
			
		||||
                  </li>
 | 
			
		||||
                  <li>この操作は元に戻すことができません</li>
 | 
			
		||||
                </ul>
 | 
			
		||||
              </div>
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user