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
@@ -17,6 +17,7 @@ import { BreadcrumbSidebar } from "@/components/shared/breadcrumb-sidebar";
import { DateTooltip } from "@/components/shared/date-tooltip"; import { DateTooltip } from "@/components/shared/date-tooltip";
import { FocusShortcutInput } from "@/components/shared/focus-shortcut-input"; import { FocusShortcutInput } from "@/components/shared/focus-shortcut-input";
import { StatusTooltip } from "@/components/shared/status-tooltip"; import { StatusTooltip } from "@/components/shared/status-tooltip";
import { TagBadge } from "@/components/shared/tag-badge";
import { TagFilter } from "@/components/shared/tag-filter"; import { TagFilter } from "@/components/shared/tag-filter";
import { import {
AlertDialog, AlertDialog,
@@ -29,7 +30,6 @@ import {
AlertDialogTitle, AlertDialogTitle,
AlertDialogTrigger, AlertDialogTrigger,
} from "@/components/ui/alert-dialog"; } from "@/components/ui/alert-dialog";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { import {
Card, Card,
@@ -486,23 +486,11 @@ export const ShowProjects = () => {
project.projectTags.length > 0 && ( project.projectTags.length > 0 && (
<div className="flex flex-wrap gap-1.5 mt-2"> <div className="flex flex-wrap gap-1.5 mt-2">
{project.projectTags.map((pt) => ( {project.projectTags.map((pt) => (
<Badge <TagBadge
key={pt.tag.tagId} key={pt.tag.tagId}
variant="blank" name={pt.tag.name}
style={{ color={pt.tag.color}
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>
))} ))}
</div> </div>
)} )}
@@ -5,6 +5,7 @@ import { useForm } from "react-hook-form";
import { toast } from "sonner"; import { toast } from "sonner";
import { z } from "zod"; import { z } from "zod";
import { AlertBlock } from "@/components/shared/alert-block"; import { AlertBlock } from "@/components/shared/alert-block";
import { TagBadge } from "@/components/shared/tag-badge";
import { import {
AlertDialog, AlertDialog,
AlertDialogAction, AlertDialogAction,
@@ -15,7 +16,6 @@ import {
AlertDialogHeader, AlertDialogHeader,
AlertDialogTitle, AlertDialogTitle,
} from "@/components/ui/alert-dialog"; } from "@/components/ui/alert-dialog";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { import {
Card, Card,
@@ -232,16 +232,10 @@ export const HandleTag = ({ tagId }: HandleTagProps) => {
{colorValue && ( {colorValue && (
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<span className="text-sm text-muted-foreground">Preview:</span> <span className="text-sm text-muted-foreground">Preview:</span>
<Badge <TagBadge
style={{ name={form.watch("name") || "Tag Name"}
backgroundColor: `${colorValue}33`, color={colorValue}
color: colorValue, />
borderColor: `${colorValue}66`,
}}
className="border"
>
{form.watch("name") || "Tag Name"}
</Badge>
</div> </div>
)} )}
</form> </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" className="flex items-center justify-between rounded-lg border p-3 hover:bg-accent/50 transition-colors"
> >
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<Badge <TagBadge name={tag.name} color={tag.color} />
style={{
backgroundColor: tag.color
? `${tag.color}33`
: undefined,
color: tag.color || undefined,
borderColor: tag.color ? `${tag.color}66` : undefined,
}}
className="border"
>
{tag.name}
</Badge>
{tag.color && ( {tag.color && (
<span className="text-xs text-muted-foreground font-mono"> <span className="text-xs text-muted-foreground font-mono">
{tag.color} {tag.color}
@@ -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>
);
}
+2 -12
View File
@@ -1,6 +1,7 @@
import { Filter } from "lucide-react"; import { Filter } from "lucide-react";
import * as React from "react"; import * as React from "react";
import { HandleTag } from "@/components/dashboard/settings/tags/tag-manager"; import { HandleTag } from "@/components/dashboard/settings/tags/tag-manager";
import { TagBadge } from "@/components/shared/tag-badge";
import { Badge } from "@/components/ui/badge"; import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Checkbox } from "@/components/ui/checkbox"; import { Checkbox } from "@/components/ui/checkbox";
@@ -109,18 +110,7 @@ export function TagFilter({
className="mr-2" className="mr-2"
onCheckedChange={() => handleTagToggle(tag.id)} onCheckedChange={() => handleTagToggle(tag.id)}
/> />
<Badge <TagBadge name={tag.name} color={tag.color} />
style={{
backgroundColor: tag.color
? `${tag.color}33`
: undefined,
color: tag.color || undefined,
borderColor: tag.color ? `${tag.color}66` : undefined,
}}
className="border"
>
{tag.name}
</Badge>
</CommandItem> </CommandItem>
); );
})} })}
+11 -24
View File
@@ -1,7 +1,7 @@
import { Check, ChevronsUpDown, X } from "lucide-react"; import { Check, ChevronsUpDown, X } from "lucide-react";
import * as React from "react"; import * as React from "react";
import { HandleTag } from "@/components/dashboard/settings/tags/tag-manager"; 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 { Button } from "@/components/ui/button";
import { Checkbox } from "@/components/ui/checkbox"; import { Checkbox } from "@/components/ui/checkbox";
import { import {
@@ -77,17 +77,12 @@ export function TagSelector({
<div className="flex flex-wrap gap-1 flex-1"> <div className="flex flex-wrap gap-1 flex-1">
{selectedTagObjects.length > 0 ? ( {selectedTagObjects.length > 0 ? (
selectedTagObjects.map((tag) => ( selectedTagObjects.map((tag) => (
<Badge <TagBadge
key={tag.id} key={tag.id}
variant="blank" name={tag.name}
style={{ color={tag.color}
backgroundColor: tag.color ? `${tag.color}33` : undefined, className="flex items-center gap-1 pr-1"
color: tag.color || undefined,
borderColor: tag.color ? `${tag.color}66` : undefined,
}}
className="flex items-center gap-1 pr-1 border"
> >
<span>{tag.name}</span>
<button <button
type="button" type="button"
onClick={(e) => handleTagRemove(tag.id, e)} onClick={(e) => handleTagRemove(tag.id, e)}
@@ -97,7 +92,7 @@ export function TagSelector({
<X className="h-3 w-3 hover:opacity-70" /> <X className="h-3 w-3 hover:opacity-70" />
<span className="sr-only">Remove {tag.name}</span> <span className="sr-only">Remove {tag.name}</span>
</button> </button>
</Badge> </TagBadge>
)) ))
) : ( ) : (
<span className="text-muted-foreground">{placeholder}</span> <span className="text-muted-foreground">{placeholder}</span>
@@ -132,19 +127,11 @@ export function TagSelector({
className="mr-2" className="mr-2"
onCheckedChange={() => handleTagToggle(tag.id)} onCheckedChange={() => handleTagToggle(tag.id)}
/> />
<Badge <TagBadge
variant="blank" name={tag.name}
style={{ color={tag.color}
backgroundColor: tag.color className="mr-2"
? `${tag.color}33` />
: undefined,
color: tag.color || undefined,
borderColor: tag.color ? `${tag.color}66` : undefined,
}}
className="mr-2 border"
>
{tag.name}
</Badge>
<Check <Check
className={cn( className={cn(
"ml-auto h-4 w-4", "ml-auto h-4 w-4",