Add API endpoints and frontend components for tool management and analytics
Implement CRUD operations for tools and ratings, introduce analytics endpoints, and develop frontend components for displaying tools, ratings, and analytics data. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 776963d0-f75d-42e2-a57b-cc36bdff8495 Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Event-Id: feaa4ce1-5aed-4cc0-bcea-47855b615b48 Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/0683fb79-a27c-485c-9333-5f4b288c4567/776963d0-f75d-42e2-a57b-cc36bdff8495/rx9K7bW Replit-Helium-Checkpoint-Created: true
This commit is contained in:
@@ -0,0 +1,356 @@
|
||||
@import "tailwindcss";
|
||||
@import "tw-animate-css";
|
||||
@plugin "@tailwindcss/typography";
|
||||
|
||||
@custom-variant dark (&:is(.dark *));
|
||||
|
||||
@theme inline {
|
||||
--color-background: hsl(var(--background));
|
||||
--color-foreground: hsl(var(--foreground));
|
||||
--color-border: hsl(var(--border));
|
||||
--color-input: hsl(var(--input));
|
||||
--color-ring: hsl(var(--ring));
|
||||
|
||||
--color-card: hsl(var(--card));
|
||||
--color-card-foreground: hsl(var(--card-foreground));
|
||||
--color-card-border: hsl(var(--card-border));
|
||||
|
||||
--color-popover: hsl(var(--popover));
|
||||
--color-popover-foreground: hsl(var(--popover-foreground));
|
||||
--color-popover-border: hsl(var(--popover-border));
|
||||
|
||||
--color-primary: hsl(var(--primary));
|
||||
--color-primary-foreground: hsl(var(--primary-foreground));
|
||||
--color-primary-border: var(--primary-border);
|
||||
|
||||
--color-secondary: hsl(var(--secondary));
|
||||
--color-secondary-foreground: hsl(var(--secondary-foreground));
|
||||
--color-secondary-border: var(--secondary-border);
|
||||
|
||||
--color-muted: hsl(var(--muted));
|
||||
--color-muted-foreground: hsl(var(--muted-foreground));
|
||||
--color-muted-border: var(--muted-border);
|
||||
|
||||
--color-accent: hsl(var(--accent));
|
||||
--color-accent-foreground: hsl(var(--accent-foreground));
|
||||
--color-accent-border: var(--accent-border);
|
||||
|
||||
--color-destructive: hsl(var(--destructive));
|
||||
--color-destructive-foreground: hsl(var(--destructive-foreground));
|
||||
--color-destructive-border: var(--destructive-border);
|
||||
|
||||
--color-chart-1: hsl(var(--chart-1));
|
||||
--color-chart-2: hsl(var(--chart-2));
|
||||
--color-chart-3: hsl(var(--chart-3));
|
||||
--color-chart-4: hsl(var(--chart-4));
|
||||
--color-chart-5: hsl(var(--chart-5));
|
||||
|
||||
--color-sidebar: hsl(var(--sidebar));
|
||||
--color-sidebar-foreground: hsl(var(--sidebar-foreground));
|
||||
--color-sidebar-border: hsl(var(--sidebar-border));
|
||||
--color-sidebar-primary: hsl(var(--sidebar-primary));
|
||||
--color-sidebar-primary-foreground: hsl(var(--sidebar-primary-foreground));
|
||||
--color-sidebar-primary-border: var(--sidebar-primary-border);
|
||||
--color-sidebar-accent: hsl(var(--sidebar-accent));
|
||||
--color-sidebar-accent-foreground: hsl(var(--sidebar-accent-foreground));
|
||||
--color-sidebar-accent-border: var(--sidebar-accent-border);
|
||||
--color-sidebar-ring: hsl(var(--sidebar-ring));
|
||||
|
||||
--font-sans: var(--app-font-sans);
|
||||
--font-serif: var(--app-font-serif);
|
||||
--font-mono: var(--app-font-mono);
|
||||
|
||||
--radius-sm: calc(var(--radius) - 4px);
|
||||
--radius-md: calc(var(--radius) - 2px);
|
||||
--radius-lg: var(--radius);
|
||||
--radius-xl: calc(var(--radius) + 4px);
|
||||
}
|
||||
|
||||
/* LIGHT MODE */
|
||||
:root {
|
||||
--button-outline: rgba(0,0,0, .10);
|
||||
--badge-outline: rgba(0,0,0, .05);
|
||||
|
||||
/* Automatic computation of border around primary / danger buttons */
|
||||
--opaque-button-border-intensity: -8; /* In terms of percentages */
|
||||
|
||||
/* Backgrounds applied on top of other backgrounds when hovered/active */
|
||||
--elevate-1: rgba(0,0,0, .03);
|
||||
--elevate-2: rgba(0,0,0, .08);
|
||||
|
||||
--background: 0 0% 99%;
|
||||
--foreground: 222 47% 11%;
|
||||
|
||||
--border: 214 32% 91%;
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 222 47% 11%;
|
||||
--card-border: 214 32% 91%;
|
||||
|
||||
--sidebar: 0 0% 98%;
|
||||
--sidebar-foreground: 222 47% 11%;
|
||||
--sidebar-border: 214 32% 91%;
|
||||
--sidebar-primary: 222 47% 11%;
|
||||
--sidebar-primary-foreground: 0 0% 98%;
|
||||
--sidebar-accent: 214 32% 91%;
|
||||
--sidebar-accent-foreground: 222 47% 11%;
|
||||
--sidebar-ring: 222 47% 11%;
|
||||
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 222 47% 11%;
|
||||
--popover-border: 214 32% 91%;
|
||||
|
||||
--primary: 14 100% 57%; /* Sharp Builder Orange */
|
||||
--primary-foreground: 0 0% 100%;
|
||||
|
||||
--secondary: 210 40% 96%;
|
||||
--secondary-foreground: 222 47% 11%;
|
||||
|
||||
--muted: 210 40% 96%;
|
||||
--muted-foreground: 215 16% 47%;
|
||||
|
||||
--accent: 210 40% 96%;
|
||||
--accent-foreground: 222 47% 11%;
|
||||
|
||||
--destructive: 0 84% 60%;
|
||||
--destructive-foreground: 0 0% 100%;
|
||||
|
||||
--input: 214 32% 91%;
|
||||
--ring: 14 100% 57%;
|
||||
|
||||
--chart-1: 14 100% 57%;
|
||||
--chart-2: 222 47% 11%;
|
||||
--chart-3: 187 100% 42%;
|
||||
--chart-4: 43 100% 50%;
|
||||
--chart-5: 280 80% 50%;
|
||||
|
||||
--app-font-sans: 'Inter', sans-serif;
|
||||
--app-font-serif: Georgia, serif;
|
||||
--app-font-mono: 'JetBrains Mono', Menlo, monospace;
|
||||
--radius: 0.25rem; /* tight, slightly sharp corners */
|
||||
--shadow-2xs: 0px 1px 2px 0px rgba(0,0,0,0.05);
|
||||
--shadow-xs: 0px 1px 3px 0px rgba(0,0,0,0.1);
|
||||
--shadow-sm: 0px 2px 4px 0px rgba(0,0,0,0.05), 0px 1px 2px -1px rgba(0,0,0,0.1);
|
||||
--shadow: 0px 4px 6px -1px rgba(0,0,0,0.1), 0px 2px 4px -2px rgba(0,0,0,0.1);
|
||||
--shadow-md: 0px 10px 15px -3px rgba(0,0,0,0.1), 0px 4px 6px -4px rgba(0,0,0,0.1);
|
||||
--shadow-lg: 0px 20px 25px -5px rgba(0,0,0,0.1), 0px 8px 10px -6px rgba(0,0,0,0.1);
|
||||
--shadow-xl: 0px 25px 50px -12px rgba(0,0,0,0.25);
|
||||
--shadow-2xl: 0px 35px 60px -15px rgba(0,0,0,0.3);
|
||||
--tracking-normal: 0em;
|
||||
--spacing: 0.25rem;
|
||||
|
||||
/* Automatically computed borders - intensity can be controlled by the user by the --opaque-button-border-intensity setting */
|
||||
|
||||
/* Fallback for older browsers */
|
||||
--sidebar-primary-border: hsl(var(--sidebar-primary));
|
||||
--sidebar-primary-border: hsl(from hsl(var(--sidebar-primary)) h s calc(l + var(--opaque-button-border-intensity)) / alpha);
|
||||
|
||||
/* Fallback for older browsers */
|
||||
--sidebar-accent-border: hsl(var(--sidebar-accent));
|
||||
--sidebar-accent-border: hsl(from hsl(var(--sidebar-accent)) h s calc(l + var(--opaque-button-border-intensity)) / alpha);
|
||||
|
||||
/* Fallback for older browsers */
|
||||
--primary-border: hsl(var(--primary));
|
||||
--primary-border: hsl(from hsl(var(--primary)) h s calc(l + var(--opaque-button-border-intensity)) / alpha);
|
||||
|
||||
/* Fallback for older browsers */
|
||||
--secondary-border: hsl(var(--secondary));
|
||||
--secondary-border: hsl(from hsl(var(--secondary)) h s calc(l + var(--opaque-button-border-intensity)) / alpha);
|
||||
|
||||
/* Fallback for older browsers */
|
||||
--muted-border: hsl(var(--muted));
|
||||
--muted-border: hsl(from hsl(var(--muted)) h s calc(l + var(--opaque-button-border-intensity)) / alpha);
|
||||
|
||||
/* Fallback for older browsers */
|
||||
--accent-border: hsl(var(--accent));
|
||||
--accent-border: hsl(from hsl(var(--accent)) h s calc(l + var(--opaque-button-border-intensity)) / alpha);
|
||||
|
||||
/* Fallback for older browsers */
|
||||
--destructive-border: hsl(var(--destructive));
|
||||
--destructive-border: hsl(from hsl(var(--destructive)) h s calc(l + var(--opaque-button-border-intensity)) / alpha);
|
||||
}
|
||||
|
||||
.dark {
|
||||
--button-outline: rgba(255,255,255, .10);
|
||||
--badge-outline: rgba(255,255,255, .05);
|
||||
|
||||
--opaque-button-border-intensity: 9; /* In terms of percentages */
|
||||
|
||||
/* Backgrounds applied on top of other backgrounds when hovered/active */
|
||||
--elevate-1: rgba(255,255,255, .04);
|
||||
--elevate-2: rgba(255,255,255, .09);
|
||||
|
||||
--background: 222 47% 11%;
|
||||
--foreground: 210 40% 98%;
|
||||
|
||||
--border: 217 33% 17%;
|
||||
--card: 222 47% 11%;
|
||||
--card-foreground: 210 40% 98%;
|
||||
--card-border: 217 33% 17%;
|
||||
|
||||
--sidebar: 222 47% 11%;
|
||||
--sidebar-foreground: 210 40% 98%;
|
||||
--sidebar-border: 217 33% 17%;
|
||||
--sidebar-primary: 210 40% 98%;
|
||||
--sidebar-primary-foreground: 222 47% 11%;
|
||||
--sidebar-accent: 217 33% 17%;
|
||||
--sidebar-accent-foreground: 210 40% 98%;
|
||||
--sidebar-ring: 210 40% 98%;
|
||||
|
||||
--popover: 222 47% 11%;
|
||||
--popover-foreground: 210 40% 98%;
|
||||
--popover-border: 217 33% 17%;
|
||||
|
||||
--primary: 14 100% 57%;
|
||||
--primary-foreground: 0 0% 100%;
|
||||
|
||||
--secondary: 217 33% 17%;
|
||||
--secondary-foreground: 210 40% 98%;
|
||||
|
||||
--muted: 217 33% 17%;
|
||||
--muted-foreground: 215 20.2% 65.1%;
|
||||
|
||||
--accent: 217 33% 17%;
|
||||
--accent-foreground: 210 40% 98%;
|
||||
|
||||
--destructive: 0 62.8% 30.6%;
|
||||
--destructive-foreground: 210 40% 98%;
|
||||
|
||||
--input: 217 33% 17%;
|
||||
--ring: 14 100% 57%;
|
||||
--chart-1: 14 100% 57%;
|
||||
--chart-2: 210 40% 98%;
|
||||
--chart-3: 187 100% 42%;
|
||||
--chart-4: 43 100% 50%;
|
||||
--chart-5: 280 80% 50%;
|
||||
|
||||
--shadow-2xs: 0px 1px 2px 0px rgba(0,0,0,0.5);
|
||||
--shadow-xs: 0px 1px 3px 0px rgba(0,0,0,0.5);
|
||||
--shadow-sm: 0px 2px 4px 0px rgba(0,0,0,0.5), 0px 1px 2px -1px rgba(0,0,0,0.5);
|
||||
--shadow: 0px 4px 6px -1px rgba(0,0,0,0.5), 0px 2px 4px -2px rgba(0,0,0,0.5);
|
||||
--shadow-md: 0px 10px 15px -3px rgba(0,0,0,0.5), 0px 4px 6px -4px rgba(0,0,0,0.5);
|
||||
--shadow-lg: 0px 20px 25px -5px rgba(0,0,0,0.5), 0px 8px 10px -6px rgba(0,0,0,0.5);
|
||||
--shadow-xl: 0px 25px 50px -12px rgba(0,0,0,0.75);
|
||||
--shadow-2xl: 0px 35px 60px -15px rgba(0,0,0,0.75);
|
||||
}
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
@apply border-border;
|
||||
}
|
||||
|
||||
body {
|
||||
@apply font-sans antialiased bg-background text-foreground;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Using the elevate system.
|
||||
* Automatic contrast adjustment.
|
||||
*
|
||||
* <element className="hover-elevate" />
|
||||
* <element className="active-elevate-2" />
|
||||
*
|
||||
* // Using the tailwind utility when a data attribute is "on"
|
||||
* <element className="toggle-elevate data-[state=on]:toggle-elevated" />
|
||||
* // Or manually controlling the toggle state
|
||||
* <element className="toggle-elevate toggle-elevated" />
|
||||
*
|
||||
* Elevation systems have to handle many states.
|
||||
* - not-hovered, vs. hovered vs. active (three mutually exclusive states)
|
||||
* - toggled or not
|
||||
* - focused or not (this is not handled with these utilities)
|
||||
*
|
||||
* Even without handling focused or not, this is six possible combinations that
|
||||
* need to be distinguished from eachother visually.
|
||||
*/
|
||||
@layer utilities {
|
||||
|
||||
/* Hide ugly search cancel button in Chrome until we can style it properly */
|
||||
input[type="search"]::-webkit-search-cancel-button {
|
||||
@apply hidden;
|
||||
}
|
||||
|
||||
/* Placeholder styling for contentEditable div */
|
||||
[contenteditable][data-placeholder]:empty::before {
|
||||
content: attr(data-placeholder);
|
||||
color: hsl(var(--muted-foreground));
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* .no-default-hover-elevate/no-default-active-elevate is an escape hatch so consumers of
|
||||
* buttons/badges can remove the automatic brightness adjustment on interactions
|
||||
* and program their own. */
|
||||
.no-default-hover-elevate {}
|
||||
|
||||
.no-default-active-elevate {}
|
||||
|
||||
|
||||
/**
|
||||
* Toggleable backgrounds go behind the content. Hoverable/active goes on top.
|
||||
* This way they can stack/compound. Both will overlap the parent's borders!
|
||||
* So borders will be automatically adjusted both on toggle, and hover/active,
|
||||
* and they will be compounded.
|
||||
*/
|
||||
.toggle-elevate::before,
|
||||
.toggle-elevate-2::before {
|
||||
content: "";
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
inset: 0px;
|
||||
/*border-radius: inherit; match rounded corners */
|
||||
border-radius: inherit;
|
||||
z-index: -1;
|
||||
/* sits behind content but above backdrop */
|
||||
}
|
||||
|
||||
.toggle-elevate.toggle-elevated::before {
|
||||
background-color: var(--elevate-2);
|
||||
}
|
||||
|
||||
/* If there's a 1px border, adjust the inset so that it covers that parent's border */
|
||||
.border.toggle-elevate::before {
|
||||
inset: -1px;
|
||||
}
|
||||
|
||||
/* Does not work on elements with overflow:hidden! */
|
||||
.hover-elevate:not(.no-default-hover-elevate),
|
||||
.active-elevate:not(.no-default-active-elevate),
|
||||
.hover-elevate-2:not(.no-default-hover-elevate),
|
||||
.active-elevate-2:not(.no-default-active-elevate) {
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.hover-elevate:not(.no-default-hover-elevate)::after,
|
||||
.active-elevate:not(.no-default-active-elevate)::after,
|
||||
.hover-elevate-2:not(.no-default-hover-elevate)::after,
|
||||
.active-elevate-2:not(.no-default-active-elevate)::after {
|
||||
content: "";
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
inset: 0px;
|
||||
/*border-radius: inherit; match rounded corners */
|
||||
border-radius: inherit;
|
||||
z-index: 999;
|
||||
/* sits in front of content */
|
||||
}
|
||||
|
||||
.hover-elevate:hover:not(.no-default-hover-elevate)::after,
|
||||
.active-elevate:active:not(.no-default-active-elevate)::after {
|
||||
background-color: var(--elevate-1);
|
||||
}
|
||||
|
||||
.hover-elevate-2:hover:not(.no-default-hover-elevate)::after,
|
||||
.active-elevate-2:active:not(.no-default-active-elevate)::after {
|
||||
background-color: var(--elevate-2);
|
||||
}
|
||||
|
||||
/* If there's a 1px border, adjust the inset so that it covers that parent's border */
|
||||
.border.hover-elevate:not(.no-hover-interaction-elevate)::after,
|
||||
.border.active-elevate:not(.no-active-interaction-elevate)::after,
|
||||
.border.hover-elevate-2:not(.no-hover-interaction-elevate)::after,
|
||||
.border.active-elevate-2:not(.no-active-interaction-elevate)::after,
|
||||
.border.hover-elevate:not(.no-hover-interaction-elevate)::after {
|
||||
inset: -1px;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user