refactor: extract TagBadge into shared component

Replace duplicated inline badge styling with a reusable TagBadge
component to ensure consistent appearance across all tag displays.
This commit is contained in:
Andrey Onishchenko
2026-02-13 19:23:32 +03:00
parent e049352f6d
commit 1da9ef8e69
5 changed files with 49 additions and 76 deletions

View File

@@ -17,6 +17,7 @@ import { BreadcrumbSidebar } from "@/components/shared/breadcrumb-sidebar";
import { DateTooltip } from "@/components/shared/date-tooltip";
import { FocusShortcutInput } from "@/components/shared/focus-shortcut-input";
import { StatusTooltip } from "@/components/shared/status-tooltip";
import { TagBadge } from "@/components/shared/tag-badge";
import { TagFilter } from "@/components/shared/tag-filter";
import {
AlertDialog,
@@ -29,7 +30,6 @@ import {
AlertDialogTitle,
AlertDialogTrigger,
} from "@/components/ui/alert-dialog";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import {
Card,
@@ -486,23 +486,11 @@ export const ShowProjects = () => {
project.projectTags.length > 0 && (
<div className="flex flex-wrap gap-1.5 mt-2">
{project.projectTags.map((pt) => (
<Badge
<TagBadge
key={pt.tag.tagId}
variant="blank"
style={{
backgroundColor: pt.tag.color
? `${pt.tag.color}33`
: undefined,
color:
pt.tag.color || undefined,
borderColor: pt.tag.color
? `${pt.tag.color}66`
: undefined,
}}
className="border"
>
{pt.tag.name}
</Badge>
name={pt.tag.name}
color={pt.tag.color}
/>
))}
</div>
)}

View File

@@ -5,6 +5,7 @@ import { useForm } from "react-hook-form";
import { toast } from "sonner";
import { z } from "zod";
import { AlertBlock } from "@/components/shared/alert-block";
import { TagBadge } from "@/components/shared/tag-badge";
import {
AlertDialog,
AlertDialogAction,
@@ -15,7 +16,6 @@ import {
AlertDialogHeader,
AlertDialogTitle,
} from "@/components/ui/alert-dialog";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import {
Card,
@@ -232,16 +232,10 @@ export const HandleTag = ({ tagId }: HandleTagProps) => {
{colorValue && (
<div className="flex items-center gap-2">
<span className="text-sm text-muted-foreground">Preview:</span>
<Badge
style={{
backgroundColor: `${colorValue}33`,
color: colorValue,
borderColor: `${colorValue}66`,
}}
className="border"
>
{form.watch("name") || "Tag Name"}
</Badge>
<TagBadge
name={form.watch("name") || "Tag Name"}
color={colorValue}
/>
</div>
)}
</form>
@@ -355,18 +349,7 @@ export const TagManager = () => {
className="flex items-center justify-between rounded-lg border p-3 hover:bg-accent/50 transition-colors"
>
<div className="flex items-center gap-3">
<Badge
style={{
backgroundColor: tag.color
? `${tag.color}33`
: undefined,
color: tag.color || undefined,
borderColor: tag.color ? `${tag.color}66` : undefined,
}}
className="border"
>
{tag.name}
</Badge>
<TagBadge name={tag.name} color={tag.color} />
{tag.color && (
<span className="text-xs text-muted-foreground font-mono">
{tag.color}

View File

@@ -0,0 +1,25 @@
import { Badge } from "@/components/ui/badge";
import { cn } from "@/lib/utils";
interface TagBadgeProps {
name: string;
color?: string | null;
className?: string;
children?: React.ReactNode;
}
export function TagBadge({ name, color, className, children }: TagBadgeProps) {
return (
<Badge
style={{
backgroundColor: color ? `${color}33` : undefined,
color: color || undefined,
borderColor: color ? `${color}66` : undefined,
}}
className={cn("border", className)}
>
{name}
{children}
</Badge>
);
}

View File

@@ -1,6 +1,7 @@
import { Filter } from "lucide-react";
import * as React from "react";
import { HandleTag } from "@/components/dashboard/settings/tags/tag-manager";
import { TagBadge } from "@/components/shared/tag-badge";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Checkbox } from "@/components/ui/checkbox";
@@ -109,18 +110,7 @@ export function TagFilter({
className="mr-2"
onCheckedChange={() => handleTagToggle(tag.id)}
/>
<Badge
style={{
backgroundColor: tag.color
? `${tag.color}33`
: undefined,
color: tag.color || undefined,
borderColor: tag.color ? `${tag.color}66` : undefined,
}}
className="border"
>
{tag.name}
</Badge>
<TagBadge name={tag.name} color={tag.color} />
</CommandItem>
);
})}

View File

@@ -1,7 +1,7 @@
import { Check, ChevronsUpDown, X } from "lucide-react";
import * as React from "react";
import { HandleTag } from "@/components/dashboard/settings/tags/tag-manager";
import { Badge } from "@/components/ui/badge";
import { TagBadge } from "@/components/shared/tag-badge";
import { Button } from "@/components/ui/button";
import { Checkbox } from "@/components/ui/checkbox";
import {
@@ -77,17 +77,12 @@ export function TagSelector({
<div className="flex flex-wrap gap-1 flex-1">
{selectedTagObjects.length > 0 ? (
selectedTagObjects.map((tag) => (
<Badge
<TagBadge
key={tag.id}
variant="blank"
style={{
backgroundColor: tag.color ? `${tag.color}33` : undefined,
color: tag.color || undefined,
borderColor: tag.color ? `${tag.color}66` : undefined,
}}
className="flex items-center gap-1 pr-1 border"
name={tag.name}
color={tag.color}
className="flex items-center gap-1 pr-1"
>
<span>{tag.name}</span>
<button
type="button"
onClick={(e) => handleTagRemove(tag.id, e)}
@@ -97,7 +92,7 @@ export function TagSelector({
<X className="h-3 w-3 hover:opacity-70" />
<span className="sr-only">Remove {tag.name}</span>
</button>
</Badge>
</TagBadge>
))
) : (
<span className="text-muted-foreground">{placeholder}</span>
@@ -132,19 +127,11 @@ export function TagSelector({
className="mr-2"
onCheckedChange={() => handleTagToggle(tag.id)}
/>
<Badge
variant="blank"
style={{
backgroundColor: tag.color
? `${tag.color}33`
: undefined,
color: tag.color || undefined,
borderColor: tag.color ? `${tag.color}66` : undefined,
}}
className="mr-2 border"
>
{tag.name}
</Badge>
<TagBadge
name={tag.name}
color={tag.color}
className="mr-2"
/>
<Check
className={cn(
"ml-auto h-4 w-4",