PageSourceSearch

https://www.pourify.com/assets/BrandAssets-DLW2Jjdo.js

js pourify.com collected 2026-10-04 19:37:33 UTC 9,125 bytes, 188 lines download raw bytes

1const e=`import { useState } from "react";
2import { Download, Search, Trash2 } from "lucide-react";
3import { Input } from "@/components/ui/input";
4import { Button } from "@/components/ui/button";
5
6import cmykSwatches from "@/assets/cmyk-swatches.jpg";
7import brandColors from "@/assets/brand-colors.png";
8import pourifyAnim from "@/assets/pourify-animation.gif";
9import illus1 from "@/assets/illustration-pourify-1.png";
10import illus2 from "@/assets/illustration-pourify-2.png";
11import illus3 from "@/assets/illustration-pourify-3.png";
12import illus4 from "@/assets/illustration-pourify-4.png";
13import illus5 from "@/assets/illustration-pourify-5.png";
14import logoCombined from "@/assets/logo-pourify-combined.png";
15import logoCombinedSvg from "@/assets/logo-pourify-combined.svg";
16
17const allAssets = [
18  { name: "Smart Pourer", file: "/src/assets/smartpourer.png", category: "Product" },
19  { name: "Pourer Close-up", file: "/src/assets/pourer.png", category: "Product" },
20  { name: "Hub", file: "/src/assets/hub.png", category: "Product" },
21  { name: "Starter Kit Packaging", file: "/src/assets/packaging-starter-kit.png", category: "Packaging" },
22  { name: "5-Pack Dark", file: "/src/assets/packaging-5pack-dark.png", category: "Packaging" },
23  { name: "5-Pack Purple", file: "/src/assets/packaging-5pack-purple.png", category: "Packaging" },
24  { name: "Lifestyle - Bar", file: "/src/assets/lifestyle-bar.jpg", category: "Lifestyle" },
25  { name: "Lifestyle - Bartender", file: "/src/assets/lifestyle-bartender.jpg", category: "Lifestyle" },
26  { name: "Lifestyle - Pouring", file: "/src/assets/lifestyle-pouring.jpg", category: "Lifestyle" },
27  { name: "Dashboard", file: "/src/assets/app-dashboard-new.png", category: "Screenshots" },
28  { name: "AI Insights", file: "/src/assets/app-ai-insights.png", category: "Screenshots" },
29  { name: "POS Integration", file: "/src/assets/app-pos-products.png", category: "Screenshots" },
30  { name: "Illustration - Bartender", file: illus1, category: "Illustrations" },
31  { name: "Illustration - Bar Scene", file: illus2, category: "Illustrations" },
32  { name: "Illustration - Cocktail", file: illus3, category: "Illustrations" },
33  { name: "Illustration - Party", file: illus4, category: "Illustrations" },
34  { name: "Illustration - Bar Shelf", file: illus5, category: "Illustrations" },
35  { name: "Brand Animation (GIF)", file: pourifyAnim, category: "Animations" },
36  { name: "Brand Animation (MP4)", file: "/videos/pourify-brand-animation.mp4", category: "Animations" },
37  { name: "CMYK Swatches", file: cmykSwatches, category: "Color References" },
38  { name: "Brand Color Strips", file: brandColors, category: "Color References" },
39  { name: "Logo Combined (PNG)", file: logoCombined, category: "Logos" },
40  { name: "Logo Combined (SVG)", file: logoCombinedSvg, category: "Logos" },
41  { name: "Roobert Regular (WOFF2)", file: "/fonts/Roobert-Regular.woff2", category: "Fonts" },
42  { name: "Roobert Regular (OTF)", file: "/fonts/Roobert-Regular.otf", category: "Fonts" },
43  { name: "Roobert Medium (WOFF2)", file: "/fonts/Roobert-Medium.woff2", category: "Fonts" },
44  { name: "Roobert Medium (OTF)", file: "/fonts/Roobert-Medium.otf", category: "Fonts" },
45  { name: "Roobert SemiBold (WOFF2)", file: "/fonts/Roobert-SemiBold.woff2", category: "Fonts" },
46  { name: "Roobert SemiBold (OTF)", file: "/fonts/Roobert-SemiBold.otf", category: "Fonts" },
47  { name: "Reckless Regular (WOFF2)", file: "/fonts/Reckless-Regular.woff2", category: "Fonts" },
48  { name: "Reckless Regular (OTF)", file: "/fonts/Reckless-Regular.otf", category: "Fonts" },
49  { name: "Reckless Light (WOFF2)", file: "/fonts/Reckless-Light.woff2", category: "Fonts" },
50  { name: "Reckless Light (OTF)", file: "/fonts/Reckless-Light.otf", category: "Fonts" },
51];
52
53const categories = ["All", "Product", "Packaging", "Lifestyle", "Screenshots", "Illustrations", "Animations", "Color References", "Logos", "Fonts"];
54
55interface BrandAssetsProps {
56  isAdmin?: boolean;
57}
58
59const BrandAssets = ({ isAdmin = false }: BrandAssetsProps) => {
60  const [search, setSearch] = useState("");
61  const [category, setCategory] = useState("All");
62  const [hiddenAssets, setHiddenAssets] = useState<Set<string>>(() => {
63    try {
64      const stored = localStorage.getItem("brand-hidden-assets");
65      return stored ? new Set(JSON.parse(stored)) : new Set();
66    } catch {
67      return new Set();
68    }
69  });
70
71  const assets = allAssets.filter((a) => !hiddenAssets.has(a.name));
72
73  const filtered = assets.filter((a) => {
74    const matchCat = category === "All" || a.category === category;
75    const matchSearch = a.name.toLowerCase().includes(search.toLowerCase());
76    return matchCat && matchSearch;
77  });
78
79  const download = (url: string, name: string) => {
80    const a = document.createElement("a");
81    a.href = url;
82    a.download = name;
83    a.click();
84  };
85
86  const hideAsset = (name: string) => {
87    if (!window.confirm(\`Are you sure you want to remove "\${name}" from the asset library?\`)) return;
88    const next = new Set(hiddenAssets);
89    next.add(name);
90    setHiddenAssets(next);
91    localStorage.setItem("brand-hidden-assets", JSON.stringify([...next]));
92  };
93
94  const restoreAll = () => {
95    setHiddenAssets(new Set());
96    localStorage.removeItem("brand-hidden-assets");
97  };
98
99  return (
100    <section>
101      <div className="flex items-center justify-between mb-1">
102        <h2 className="text-lg font-semibold text-foreground">Marketing Assets</h2>
103        {isAdmin && hiddenAssets.size > 0 && (
104          <Button variant="ghost" size="sm" onClick={restoreAll} className="text-xs text-muted-foreground">
105            Restore {hiddenAssets.size} hidden
106          </Button>
107        )}
108      </div>
109      <p className="text-muted-foreground text-sm mb-6">Product photos, packaging, lifestyle imagery, and app screenshots.</p>
110
111      <div className="flex flex-col sm:flex-row gap-3 mb-6">
112        <div className="relative flex-1">
113          <Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground/50" />
114          <Input
115            value={search}
116            onChange={(e) => setSearch(e.target.value)}
117            placeholder="Search assets…"
118            className="pl-10 h-9 rounded-lg"
119          />
120        </div>
121        <div className="flex gap-1.5 flex-wrap">
122          {categories.map((c) => (
123            <button
124              key={c}
125              onClick={() => setCategory(c)}
126              className={\`px-3 py-1.5 rounded-full text-xs font-medium transition-colors \${
127                category === c
128                  ? "bg-primary text-primary-foreground"
129                  : "bg-muted text-muted-foreground hover:text-foreground"
130              }\`}
131            >
132              {c}
133            </button>
134          ))}
135        </div>
136      </div>
137
138      <div className="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-3">
139        {filtered.map((a) => (
140          <div key={a.name} className="bg-card border border-border rounded-xl overflow-hidden group relative">
141            <div className="bg-muted/50 h-36 flex items-center justify-center p-4">
142              {a.category === "Fonts" ? (
143                <div className="text-center">
144                  <Download className="h-8 w-8 text-muted-foreground/40 mx-auto mb-1" />
145                  <p className="text-muted-foreground text-[10px]">Font file</p>
146                </div>
147              ) : a.file.endsWith(".mp4") ? (
148                <video src={a.file} className="max-h-full max-w-full object-contain" muted autoPlay loop playsInline />
149              ) : (
150                <img src={a.file} alt={a.name} className="max-h-full max-w-full object-contain" />
151              )}
152            </div>
153            <div className="p-3 flex items-center justify-between">
154              <div className="min-w-0">
155                <p className="text-foreground/70 text-xs font-medium truncate">{a.name}</p>
156                <p className="text-muted-foreground text-[10px] mt-0.5">{a.category}</p>
157              </div>
158              <div className="flex items-center gap-0.5 shrink-0">
159                <Button
160                  size="sm"
161                  variant="ghost"
162                  className="text-muted-foreground hover:text-foreground h-7 w-7 p-0 opacity-0 group-hover:opacity-100 transition-opacity"
163                  onClick={() => download(a.file, a.file.split("/").pop()!)}
164                >
165                  <Download className="h-3.5 w-3.5" />
166                </Button>
167                {isAdmin && (
168                  <Button
169                    size="sm"
170                    variant="ghost"
171                    className="text-muted-foreground hover:text-destructive h-7 w-7 p-0 opacity-0 group-hover:opacity-100 transition-opacity"
172                    onClick={() => hideAsset(a.name)}
173                    title="Remove asset"
174                  >
175                    <Trash2 className="h-3.5 w-3.5" />
176                  </Button>
177                )}
178              </div>
179            </div>
180          </div>
181        ))}
182      </div>
183    </section>
184  );
185};
186
187export default BrandAssets;
188`;export{e as default};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.