diff --git a/.vscode/settings.json b/.vscode/settings.json
index dbda57af7d..22f07fabf7 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -26,5 +26,8 @@
"**/.turbo": true,
"**/node_modules": true,
"**/pnpm-lock.yaml": true
+ },
+ "[typescript]": {
+ "editor.defaultFormatter": "oxc.oxc-vscode"
}
}
diff --git a/apps/cms/schemas/index.ts b/apps/cms/schemas/index.ts
index 85bc13ea6c..b5552e4223 100644
--- a/apps/cms/schemas/index.ts
+++ b/apps/cms/schemas/index.ts
@@ -12,11 +12,13 @@ import contactProfile from "./objects/contact-profile";
import question from "./objects/question";
import spotRange from "./objects/spot-range";
import time from "./objects/time";
+import trophy from "./objects/trophy";
import post from "./post";
import profile from "./profile";
import repeatingHappening from "./repeating-happening";
import staticInfo from "./static-info";
import studentGroup from "./student-group";
+import trophies from "./trophies";
export const schemaTypes = [
hsApplication,
@@ -38,4 +40,6 @@ export const schemaTypes = [
time,
merch,
hungerGames,
+ trophies,
+ trophy,
];
diff --git a/apps/cms/schemas/objects/trophy.tsx b/apps/cms/schemas/objects/trophy.tsx
new file mode 100644
index 0000000000..9e35def6c3
--- /dev/null
+++ b/apps/cms/schemas/objects/trophy.tsx
@@ -0,0 +1,55 @@
+import { defineField, defineType } from "sanity";
+
+export default defineType({
+ name: "trophy",
+ title: "Trofé",
+ type: "object",
+ fields: [
+ defineField({
+ name: "image",
+ title: "Bilde",
+ type: "image",
+ description: "Last opp et bilde for dette spesifikke trofeet.",
+ options: { hotspot: true },
+ validation: (Rule) => Rule.required(),
+ }),
+ defineField({
+ name: "title",
+ title: "Tittel",
+ type: "string",
+ description: "Fyll inn en spesifikk tittel til dette troféet",
+ placeholder: "F.eks. 'Øvelseskjøring' eller 'Sølv gokart'",
+ validation: (Rule) => Rule.required(),
+ }),
+ defineField({
+ name: "description",
+ title: "Beskrivelse",
+ type: "text",
+ rows: 3,
+ placeholder:
+ "F.eks 'Du har vært med på ett arrangement, bli med ett til for å få gull trofé.'",
+ validation: (Rule) => Rule.required(),
+ }),
+ defineField({
+ name: "level",
+ title: "Nivå",
+ type: "number",
+ description:
+ "Talled du gir til nivå vil ikke ha noe å si. Om du ønske flere nivå, betyr lavere tall lavere nivå.",
+ placeholder: "1",
+ validation: (Rule) => Rule.required().integer().min(1),
+ }),
+ ],
+ preview: {
+ select: { title: "title", media: "image", level: "level" },
+ prepare({ title, media, level }) {
+ return {
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
+ title: title ?? "Uten tittel",
+ subtitle: level ? `Nivå ${level}` : "Uten nivå",
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
+ media,
+ };
+ },
+ },
+});
diff --git a/apps/cms/schemas/trophies.tsx b/apps/cms/schemas/trophies.tsx
new file mode 100644
index 0000000000..9a5198826f
--- /dev/null
+++ b/apps/cms/schemas/trophies.tsx
@@ -0,0 +1,70 @@
+import { StarIcon } from "@sanity/icons";
+import { defineArrayMember, defineField, defineType } from "sanity";
+
+export default defineType({
+ name: "trophies",
+ title: "Troféer",
+ type: "document",
+ icon: StarIcon,
+ fields: [
+ defineField({
+ name: "title",
+ title: "Tittel",
+ type: "string",
+ description: "Dette blir en felles tittel, uansett om du har oppnådd troféet eller ikke.",
+ placeholder: "F.eks. Gokart",
+ validation: (Rule) => Rule.required(),
+ }),
+ defineField({
+ name: "slug",
+ title: "Slug",
+ type: "slug",
+ description: "Genereres automatisk fra tittel",
+ options: { source: "title", maxLength: 96 },
+ validation: (Rule) => Rule.required(),
+ }),
+ defineField({
+ name: "baseImage",
+ title: "Bilde",
+ type: "image",
+ options: { hotspot: true },
+ description: "Dette bildet vises når en bruker ikke har oppnådd dette troféet",
+ validation: (Rule) => Rule.required(),
+ }),
+ defineField({
+ name: "baseDescription",
+ title: "Beskrivelse",
+ type: "text",
+ rows: 3,
+ description: "Denne teksten vises når en bruker ikke har oppnådd dette troféet",
+ placeholder: "F.eks. 'Bli med på gokart for å få dette troféet'",
+ validation: (Rule) => Rule.required(),
+ }),
+ defineField({
+ name: "trophies",
+ title: "Legg til trofé",
+ type: "array",
+ description:
+ "Dersom du kunn ønsker at det skal være ett nivå, legg til ett objekt. Om du ønsker flere nivå, f.eks. gull, sølv og bronsje, kan du legge til flere objekt.",
+ of: [defineArrayMember({ type: "trophy" })],
+ options: { sortable: true },
+ validation: (Rule) => Rule.min(0),
+ }),
+ ],
+ preview: {
+ select: {
+ title: "title",
+ media: "baseImage",
+ count: "trophies.length",
+ },
+ prepare({ title, media, count }) {
+ return {
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
+ title: title ?? "Trofé-side",
+ subtitle: typeof count === "number" ? `${count} trofé(er)` : "Ingen troféer enda",
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
+ media,
+ };
+ },
+ },
+});
diff --git a/apps/uno/bootstrap/api.go b/apps/uno/bootstrap/api.go
index 636d302976..2ef8925a16 100644
--- a/apps/uno/bootstrap/api.go
+++ b/apps/uno/bootstrap/api.go
@@ -87,6 +87,7 @@ func RunApi() {
cmsMeetingMinuteRepo := sanityinfra.NewMeetingMinuteRepo(sanityClient, logger, redisClient)
cmsMovieRepo := sanityinfra.NewMovieRepo(sanityClient, logger, redisClient)
cmsHSApplicationRepo := sanityinfra.NewHSApplicationRepo(sanityClient, logger, redisClient)
+ cmsTrophyRepo := sanityinfra.NewTrophyRepo(sanityClient, logger, redisClient)
// Initialize services
feideProvider := providers.NewFeideProvider(providers.FeideConfig{
@@ -130,6 +131,7 @@ func RunApi() {
cmsMeetingMinuteRepo,
cmsMovieRepo,
cmsHSApplicationRepo,
+ cmsTrophyRepo,
cacheInvalidator,
)
diff --git a/apps/uno/domain/model/cms_trophy.go b/apps/uno/domain/model/cms_trophy.go
new file mode 100644
index 0000000000..635bec7217
--- /dev/null
+++ b/apps/uno/domain/model/cms_trophy.go
@@ -0,0 +1,18 @@
+package model
+
+type CMSTrophyCollection struct {
+ ID string `json:"_id"`
+ Title string `json:"title"`
+ Slug string `json:"slug"`
+ BaseImage Image `json:"baseImage"`
+ BaseDescription string `json:"baseDescription"`
+ Trophies []CMSTrophy `json:"trophies"`
+}
+
+type CMSTrophy struct {
+ Key string `json:"_key"`
+ Title string `json:"title"`
+ Description string `json:"description"`
+ Level string `json:"level"`
+ Image Image `json:"image"`
+}
\ No newline at end of file
diff --git a/apps/uno/domain/port/cms.go b/apps/uno/domain/port/cms.go
index 135cac1747..f87c37a75f 100644
--- a/apps/uno/domain/port/cms.go
+++ b/apps/uno/domain/port/cms.go
@@ -64,3 +64,7 @@ type CMSMovieRepo interface {
type CMSHSApplicationRepo interface {
GetAllHSApplications(ctx context.Context) ([]model.CMSHSApplication, error)
}
+
+type CMSTrophyRepo interface {
+ GetAllTrophies(ctx context.Context) ([]model.CMSTrophy, error)
+}
\ No newline at end of file
diff --git a/apps/uno/domain/service/cms.go b/apps/uno/domain/service/cms.go
index eea8569b4d..bb57b4ef4a 100644
--- a/apps/uno/domain/service/cms.go
+++ b/apps/uno/domain/service/cms.go
@@ -22,6 +22,7 @@ var cmsTypeNamespaces = map[string][]string{
"merch": {sanityinfra.CMSMerchNamespaceMerch},
"meetingMinute": {sanityinfra.CMSMeetingMinuteNamespaceMeetingMinutes},
"movie": {sanityinfra.CMSMovieNamespaceMovies},
+ "trophy": {sanityinfra.CMSTrophyNamespaceTrophies},
}
type CMSService struct {
@@ -36,6 +37,7 @@ type CMSService struct {
meetingMinuteRepo port.CMSMeetingMinuteRepo
movieRepo port.CMSMovieRepo
hsApplicationRepo port.CMSHSApplicationRepo
+ trophyRepo port.CMSTrophyRepo
invalidator port.CacheInvalidator
}
@@ -52,6 +54,7 @@ func NewCMSService(
meetingMinuteRepo port.CMSMeetingMinuteRepo,
movieRepo port.CMSMovieRepo,
hsApplicationRepo port.CMSHSApplicationRepo,
+ trophyRepo port.CMSTrophyRepo,
invalidator port.CacheInvalidator,
) *CMSService {
return &CMSService{
@@ -65,6 +68,7 @@ func NewCMSService(
merchRepo: merchRepo,
meetingMinuteRepo: meetingMinuteRepo,
movieRepo: movieRepo,
+ trophyRepo: trophyRepo,
hsApplicationRepo: hsApplicationRepo,
invalidator: invalidator,
@@ -168,6 +172,10 @@ func (s *CMSService) GetUpcomingMovies(ctx context.Context, n int) ([]model.CMSM
return s.movieRepo.GetUpcomingMovies(ctx, n)
}
+func (s *CMSService) GetAllTrophies(ctx context.Context) ([]model.CMSTrophy, error) {
+ return s.trophyRepo.GetAllTrophies(ctx)
+}
+
// CMSHappeningFilter holds filter parameters for GetFilteredHappenings.
type CMSHappeningFilter struct {
Search string
diff --git a/apps/uno/http/routes/api/sanity_cms.go b/apps/uno/http/routes/api/sanity_cms.go
index 6a93891982..2c62909cea 100644
--- a/apps/uno/http/routes/api/sanity_cms.go
+++ b/apps/uno/http/routes/api/sanity_cms.go
@@ -68,6 +68,8 @@ func NewSanityMux(
mux.GET("/hs-applications", s.getAllHSApplications)
+ mux.GET("/trophies", s.getAllTrophies)
+
return mux
}
@@ -433,6 +435,27 @@ func (s *sanityCMS) getAllMovies(ctx *handler.Context) error {
return ctx.JSON(movies)
}
+// getAllTrophies returns all trophies from Sanity CMS
+// @Summary Get all trophies from CMS
+// @Tags sanity
+// @Produce json
+// @Success 200 {array} dto.CMSTrophyDTO "OK"
+// @Failure 500 {string} string "Internal Server Error"
+// @Router /sanity/trophies [get]
+func (s *sanityCMS) getAllTrophies(ctx *handler.Context) error {
+ trophies, err := s.cmsService.GetAllTrophies(ctx.Context())
+ s.logger.Error(ctx.Context(), fmt.Sprintf("error = %s", err.Error()))
+ s.logger.Error(ctx.Context(), fmt.Sprintf("error = %s", err.Error()))
+ s.logger.Error(ctx.Context(), fmt.Sprintf("error = %s", err.Error()))
+ s.logger.Error(ctx.Context(), fmt.Sprintf("error = %s", err.Error()))
+ s.logger.Error(ctx.Context(), fmt.Sprintf("error = %s", err.Error()))
+
+ if err != nil {
+ return ctx.InternalServerError()
+ }
+ return ctx.JSON(trophies)
+}
+
// getAllHSApplications returns all HS applications from Sanity CMS
// @Summary Get all HS applications from CMS
// @Tags sanity
diff --git a/apps/uno/infrastructure/external/sanity/trophy_repo.go b/apps/uno/infrastructure/external/sanity/trophy_repo.go
new file mode 100644
index 0000000000..5b24742ef9
--- /dev/null
+++ b/apps/uno/infrastructure/external/sanity/trophy_repo.go
@@ -0,0 +1,94 @@
+package sanityinfra
+
+import (
+ "context"
+ "uno/domain/model"
+ "uno/domain/port"
+ "uno/infrastructure/cache"
+ "uno/pkg/sanity"
+
+ "github.com/redis/go-redis/v9"
+)
+
+const CMSTrophyNamespaceTrophies = "cms:trophies"
+
+// const trophiesBySlugQuery = `
+// *[_type == "trophies" && slug.current == $slug][0]{
+// _id,
+// title,
+// "slug": slug.current,
+// baseImage,
+// baseDescription,
+// trophies[]{
+// _key,
+// title,
+// description,
+// level,
+// image
+// }
+// `;
+
+const allTrophiesQuery = `
+*[_type == "trophies"] | order(title asc) {
+ _id,
+ title,
+ "slug": slug.current,
+ baseImage,
+ baseDescription,
+ trophies[]{
+ _key,
+ title,
+ description,
+ level,
+ image
+ },
+}
+`;
+
+type TrophyRepo struct {
+ client *sanity.Client
+ logger port.Logger
+ trophyCache port.Cache[[]model.CMSTrophy]
+}
+
+func NewTrophyRepo(client *sanity.Client, logger port.Logger, redisClient *redis.Client) port.CMSTrophyRepo {
+ return &TrophyRepo{
+ client: client,
+ logger: logger,
+ trophyCache: cache.NewCache[[]model.CMSTrophy](redisClient, CMSTrophyNamespaceTrophies),
+ }
+}
+
+// func (r *TrophyRepo) GetTrophiesBySlug(ctx context.Context, groupType string) ([]model.CMSTrophy, error) {
+// r.logger.Info(ctx, "getting trophies by slug from sanity", "type", groupType)
+// trophy, err := r.GetAllTrophies(ctx)
+// if err != nil {
+// return nil, err
+// }
+
+// result := make([]model.CMSTrophy, 0, len(trophy))
+// for _, group := range trophy {
+// if group.GroupType == groupType {
+// result = append(result, group)
+// }
+// }
+// r.logger.Info(ctx, "filtered trophies by slug from all trophies cache", "type", groupType, "count", len(result))
+// return result, nil
+// }
+
+func (r *TrophyRepo) GetAllTrophies(ctx context.Context) ([]model.CMSTrophy, error) {
+ r.logger.Info(ctx, "getting all trophies from sanity")
+ if v, ok := r.trophyCache.Get("all"); ok {
+ r.logger.Info(ctx, "cache hit for all trophies")
+ return v, nil
+ }
+ r.logger.Info(ctx, "cache miss for all trophies")
+ result, err := sanity.Query[[]model.CMSTrophy](ctx, r.client, allTrophiesQuery, nil)
+ if err != nil {
+ r.logger.Error(ctx, "failed to get all Trophies from sanity", "error", err)
+ return nil, err
+ }
+
+ r.trophyCache.Set("all", result, cmsCacheTTL)
+ return result, nil
+}
\ No newline at end of file
diff --git a/apps/web/src/api/uno/client.ts b/apps/web/src/api/uno/client.ts
index 078696dafd..3e07171363 100644
--- a/apps/web/src/api/uno/client.ts
+++ b/apps/web/src/api/uno/client.ts
@@ -618,6 +618,7 @@ export class UnoClient {
quotes: QuotesApi;
sanity: SanityApi;
auth: AuthApi;
+ trophies: TrophiesApi;
constructor(options: UnoClientOptions) {
this.api = ky.create({
@@ -648,6 +649,7 @@ export class UnoClient {
this.quotes = new QuotesApi(this);
this.sanity = new SanityApi(this);
this.auth = new AuthApi(this);
+ this.trophies = new TrophiesApi(this);
}
normalizePath(path: string) {
@@ -1523,3 +1525,20 @@ class AuthApi {
return await this.client.request("GET", `auth/magic-link/verify?${query.toString()}`);
}
}
+
+class TrophiesApi {
+ private client: UnoClient;
+
+ constructor(client: UnoClient) {
+ this.client = client;
+ }
+
+ async all() {
+ const response = await this.client.request("GET", "sanity/trophies");
+ if (!response.ok) {
+ console.log("resp = ", await response.text());
+ return [];
+ }
+ return await response.json();
+ }
+}
diff --git a/apps/web/src/app/(default)/admin/layout.tsx b/apps/web/src/app/(default)/admin/layout.tsx
index f011befe3d..2550ff2d7f 100644
--- a/apps/web/src/app/(default)/admin/layout.tsx
+++ b/apps/web/src/app/(default)/admin/layout.tsx
@@ -53,6 +53,11 @@ const adminRoutes = [
label: "Pizza",
groups: ["webkom", "hovedstyret"],
},
+ {
+ href: "/admin/trofeer",
+ label: "Troféer",
+ groups: ["webkom", "hovedstyret"],
+ },
];
export default async function AdminDashboardLayout({ children }: Props) {
diff --git a/apps/web/src/app/(default)/admin/trofeer/page.tsx b/apps/web/src/app/(default)/admin/trofeer/page.tsx
new file mode 100644
index 0000000000..f317873ec7
--- /dev/null
+++ b/apps/web/src/app/(default)/admin/trofeer/page.tsx
@@ -0,0 +1,53 @@
+import Image from "next/image";
+
+import { Container } from "@/components/container";
+import { Heading } from "@/components/typography/heading";
+import { Text } from "@/components/typography/text";
+import { ensureWebkomOrHovedstyret } from "@/lib/ensure";
+import { UserTrophiesModal } from "./usertrophies-modal";
+import { unoWithAdmin } from "../../../../api/server";
+import { urlFor } from "../../../../lib/sanity";
+
+export default async function TrophyPage() {
+ await ensureWebkomOrHovedstyret();
+
+ const trophies = await Promise.all([unoWithAdmin.trophies.all()]);
+ const [users] = await Promise.all([unoWithAdmin.users.all()]);
+
+ if (!trophies) {
+ return
Kunne ikke hente troféer.
;
+ }
+
+ return (
+
+ Troféer
+ Her er det mulig å redigere hvem som fortjener alle troféene.
+
+ {trophies.map((trophy) => (
+
+
{trophy.title}
+
+ {trophy.trophies && (
+
+ {trophy.trophies.map((t) => (
+
+
+
+
+ ))}
+
+ )}
+
+
+ ))}
+
+ );
+}
diff --git a/apps/web/src/app/(default)/admin/trofeer/usertrophies-modal.tsx b/apps/web/src/app/(default)/admin/trofeer/usertrophies-modal.tsx
new file mode 100644
index 0000000000..04585e11a6
--- /dev/null
+++ b/apps/web/src/app/(default)/admin/trofeer/usertrophies-modal.tsx
@@ -0,0 +1,83 @@
+"use client";
+
+import { useState } from "react";
+import { DialogClose } from "@radix-ui/react-dialog";
+import { CheckIcon } from "lucide-react";
+
+import { Button } from "@/components/ui/button";
+import {
+ Dialog,
+ DialogBody,
+ DialogContent,
+ DialogFooter,
+ DialogHeader,
+ DialogTitle,
+ DialogTrigger,
+} from "@/components/ui/dialog";
+import { Input } from "@/components/ui/input";
+import { Label } from "@/components/ui/label";
+import { type AllUsers } from "../brukere/page";
+
+export const UserTrophiesModal = ({ users }: { users: AllUsers }) => {
+ const [searchQuery, setSearchQuery] = useState("");
+ return (
+
+ );
+};
+
+type User = AllUsers[number];
+
+const LineForGivingTrophyToUser = ({ user }: { user: User }) => {
+ return (
+
+ );
+};
+
+const GiveTrophyButton = ({ hasTrophy }: { hasTrophy: boolean }) => {
+ return (
+
+ );
+};
diff --git a/apps/web/src/app/(default)/auth/profil/layout.tsx b/apps/web/src/app/(default)/auth/profil/layout.tsx
new file mode 100644
index 0000000000..80c93656ce
--- /dev/null
+++ b/apps/web/src/app/(default)/auth/profil/layout.tsx
@@ -0,0 +1,38 @@
+import {
+ Sidebar,
+ SidebarItem,
+ SidebarLayoutContent,
+ SidebarLayoutRoot,
+} from "@/components/sidebar-layout";
+
+const routes = [
+ {
+ label: "Profil",
+ href: "/auth/profil",
+ },
+ {
+ label: "Arrangementer",
+ href: "/auth/profil/arrangementer",
+ },
+ {
+ label: "Troféer",
+ href: "/auth/profil/trofeer",
+ },
+];
+
+export default function ProfileLayout({ children }: { children: React.ReactNode }) {
+ return (
+
+
+ {routes.map((route) => {
+ return (
+
+ {route.label}
+
+ );
+ })}
+
+ {children}
+
+ );
+}
diff --git a/apps/web/src/app/(default)/auth/user/[id]/layout.tsx b/apps/web/src/app/(default)/auth/user/[id]/layout.tsx
index 4f88f64b23..d3ea4f855f 100644
--- a/apps/web/src/app/(default)/auth/user/[id]/layout.tsx
+++ b/apps/web/src/app/(default)/auth/user/[id]/layout.tsx
@@ -16,6 +16,10 @@ const getRoutes = (profileOwnerId: string) => {
label: "Arrangementer",
href: `/auth/user/${profileOwnerId}/arrangementer`,
},
+ {
+ label: "Troféer",
+ href: `/auth/user/${profileOwnerId}/trofeer`,
+ },
];
};
diff --git a/apps/web/src/app/(default)/auth/user/[id]/trofeer/page.tsx b/apps/web/src/app/(default)/auth/user/[id]/trofeer/page.tsx
new file mode 100644
index 0000000000..e5057635ce
--- /dev/null
+++ b/apps/web/src/app/(default)/auth/user/[id]/trofeer/page.tsx
@@ -0,0 +1,60 @@
+import Image from "next/image";
+import { redirect } from "next/navigation";
+
+import { auth } from "@/auth/session";
+import { Heading } from "@/components/typography/heading";
+import {
+ Popover,
+ PopoverContent,
+ PopoverTrigger,
+} from "@/components/ui/popover";
+import { urlFor } from "../../../../../../lib/sanity";
+import { unoWithAdmin } from "../../../../../../api/server";
+
+export default async function UserTrophies() {
+ const user = await auth();
+
+ if (!user) {
+ return redirect("/auth/logg-inn");
+ }
+
+ const trophies = await Promise.all([unoWithAdmin.trophies.all()]);
+
+ if (!trophies) {
+ return Kunne ikke hente troféer.
;
+ }
+
+ return (
+
+
+ Dine troféer
+
+
Denne siden er under produksjon ...
+
+ {trophies.map((trophy) => (
+
+
+
+ {trophy.title}
+
+
+
+ {trophy.title}
+ {trophy.baseDescription}
+
+
+ ))}
+
+
+ );
+}
diff --git a/apps/web/src/app/(default)/auth/user/[id]/trofeer/trophy-helpers.ts b/apps/web/src/app/(default)/auth/user/[id]/trofeer/trophy-helpers.ts
new file mode 100644
index 0000000000..eba35b7482
--- /dev/null
+++ b/apps/web/src/app/(default)/auth/user/[id]/trofeer/trophy-helpers.ts
@@ -0,0 +1,97 @@
+import {
+ BedriftsturEmpty,
+ BedriftsturGold,
+ BrannkampBronze,
+ BrannkampEmpty,
+ BrannkampGold,
+ BrannkampSilver,
+ GokartBronze,
+ GokartEmpty,
+ GokartGold,
+ GokartSilver,
+ VinterballBronze,
+ VinterballEmpty,
+ VinterballGold,
+ VinterballSilver,
+} from "@/assets/images/trophies";
+import { getRegistrationsForTrophy, type RegistrationForTrophy } from "@/data/trophy/queries";
+import { TrophyName } from "./page";
+
+export const getUserTrophies = async (userId: string) => {
+ const registrations = await getRegistrationsForTrophy(userId);
+ const gokart = getGoKartTrophy(registrations);
+ const bedriftstur = getBedriftsturTrophy(registrations);
+ return [gokart, bedriftstur];
+};
+
+export const getGoKartTrophy = (registrations: Array) => {
+ const gokartRegistrations = registrations.filter((registration) =>
+ registration.happening.slug.includes("gokart"),
+ );
+ const name = TrophyName.GOKART;
+ const level =
+ gokartRegistrations.length >= 3
+ ? TrophyName.GOLD
+ : gokartRegistrations.length === 2
+ ? TrophyName.SILVER
+ : gokartRegistrations.length === 1
+ ? TrophyName.BRONZE
+ : TrophyName.EMPTY;
+
+ const image =
+ gokartRegistrations.length >= 3
+ ? GokartGold
+ : gokartRegistrations.length === 2
+ ? GokartSilver
+ : gokartRegistrations.length === 1
+ ? GokartBronze
+ : GokartEmpty;
+
+ const title =
+ gokartRegistrations.length >= 3
+ ? "Formel 1 sjåfør"
+ : gokartRegistrations.length === 2
+ ? "Oppkjøring"
+ : gokartRegistrations.length === 1
+ ? "Kjøretime"
+ : "Ikke lappen";
+ const description =
+ gokartRegistrations.length >= 3
+ ? "Du har deltatt på tre gokart arrangement.\n\nDu er jammen meg glad i Gokart!"
+ : gokartRegistrations.length === 2
+ ? "Du har deltatt på to gokart arrangement.\n\nDelta på én til og få gull Gokart!"
+ : gokartRegistrations.length === 1
+ ? "Du har deltatt på ett gokart arrangement.\n\nDelta på flere og få gull og bronse!"
+ : "Delta på Gokart arrangement med Gnist for å oppnå dette trofeet!";
+ return {
+ name,
+ level,
+ image,
+ title,
+ description,
+ };
+};
+
+export const getBedriftsturTrophy = (registrations: Array) => {
+ const bedriftsturRegistrations = registrations.filter((registration) =>
+ registration.happening.slug.includes("bedriftstur"),
+ );
+
+ const isGold = bedriftsturRegistrations.length >= 1;
+
+ const name = TrophyName.BEDRIFTSTUR;
+ const level = isGold ? TrophyName.GOLD : TrophyName.EMPTY;
+ const image = isGold ? BedriftsturGold : BedriftsturEmpty;
+ const title = isGold ? "Bedriftstur-veteran" : "Ikke deltatt";
+ const description = isGold
+ ? "Du har deltatt på bedriftstur med Gnist!\n\nHåper du hadde en fin tur!"
+ : "Delta på bedriftstur med Gnist for å oppnå dette trofeet!";
+
+ return {
+ name,
+ level,
+ image,
+ title,
+ description,
+ };
+};
diff --git a/apps/web/src/data/trophy/queries.ts b/apps/web/src/data/trophy/queries.ts
new file mode 100644
index 0000000000..5b85648e56
--- /dev/null
+++ b/apps/web/src/data/trophy/queries.ts
@@ -0,0 +1,24 @@
+// import { getRegistrationsByUserId } from "../registrations/queries";
+
+// export type RegistrationForTrophy = {
+// userId: string;
+// status: "registered" | "unregistered" | "removed" | "waiting" | "pending";
+// happening: {
+// slug: string;
+// title: string;
+// };
+// };
+
+// export async function getRegistrationsForTrophy(
+// userId: string,
+// ): Promise> {
+// const rows = await getRegistrationsByUserId(userId);
+// return rows.map((r) => ({
+// userId: r.userId,
+// status: r.status,
+// happening: {
+// slug: r.happening.slug,
+// title: r.happening.title,
+// },
+// }));
+// }
diff --git a/packages/db/src/schemas/user-trophies.ts b/packages/db/src/schemas/user-trophies.ts
new file mode 100644
index 0000000000..5f03544da5
--- /dev/null
+++ b/packages/db/src/schemas/user-trophies.ts
@@ -0,0 +1,32 @@
+import { type InferInsertModel, type InferSelectModel } from "drizzle-orm";
+import {
+ boolean,
+ pgTable,
+ primaryKey,
+ text,
+ varchar,
+ integer,
+} from "drizzle-orm/pg-core";
+import { createInsertSchema, createSelectSchema } from "drizzle-zod";
+
+import { users } from "./users";
+
+export const usersTrophies = pgTable(
+ "users_to_groups",
+ {
+ userId: text("user_id")
+ .notNull()
+ .references(() => users.id, {
+ onDelete: "cascade",
+ }),
+ trophyId: varchar("group_id", { length: 255 }).notNull(),
+ level: integer("trophy_level").notNull(),
+ },
+ (t) => [primaryKey({ columns: [t.userId, t.trophyId] })],
+).enableRLS();
+
+export type UsersTrophies = InferSelectModel;
+export type UsersTrophiesInsert = InferInsertModel;
+
+export const selectUsersTrophiesSchema = createSelectSchema(usersTrophies);
+export const insertUsersTrophiesSchema = createInsertSchema(usersTrophies);
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index af26db9039..77088a1854 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -2728,183 +2728,155 @@ packages:
resolution: {integrity: sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==}
cpu: [arm64]
os: [linux]
- libc: [glibc]
'@img/sharp-libvips-linux-arm64@1.2.4':
resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==}
cpu: [arm64]
os: [linux]
- libc: [glibc]
'@img/sharp-libvips-linux-arm@1.0.5':
resolution: {integrity: sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==}
cpu: [arm]
os: [linux]
- libc: [glibc]
'@img/sharp-libvips-linux-arm@1.2.4':
resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==}
cpu: [arm]
os: [linux]
- libc: [glibc]
'@img/sharp-libvips-linux-ppc64@1.2.4':
resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==}
cpu: [ppc64]
os: [linux]
- libc: [glibc]
'@img/sharp-libvips-linux-riscv64@1.2.4':
resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==}
cpu: [riscv64]
os: [linux]
- libc: [glibc]
'@img/sharp-libvips-linux-s390x@1.0.4':
resolution: {integrity: sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==}
cpu: [s390x]
os: [linux]
- libc: [glibc]
'@img/sharp-libvips-linux-s390x@1.2.4':
resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==}
cpu: [s390x]
os: [linux]
- libc: [glibc]
'@img/sharp-libvips-linux-x64@1.0.4':
resolution: {integrity: sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==}
cpu: [x64]
os: [linux]
- libc: [glibc]
'@img/sharp-libvips-linux-x64@1.2.4':
resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==}
cpu: [x64]
os: [linux]
- libc: [glibc]
'@img/sharp-libvips-linuxmusl-arm64@1.0.4':
resolution: {integrity: sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==}
cpu: [arm64]
os: [linux]
- libc: [musl]
'@img/sharp-libvips-linuxmusl-arm64@1.2.4':
resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==}
cpu: [arm64]
os: [linux]
- libc: [musl]
'@img/sharp-libvips-linuxmusl-x64@1.0.4':
resolution: {integrity: sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==}
cpu: [x64]
os: [linux]
- libc: [musl]
'@img/sharp-libvips-linuxmusl-x64@1.2.4':
resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==}
cpu: [x64]
os: [linux]
- libc: [musl]
'@img/sharp-linux-arm64@0.33.5':
resolution: {integrity: sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm64]
os: [linux]
- libc: [glibc]
'@img/sharp-linux-arm64@0.34.5':
resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm64]
os: [linux]
- libc: [glibc]
'@img/sharp-linux-arm@0.33.5':
resolution: {integrity: sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm]
os: [linux]
- libc: [glibc]
'@img/sharp-linux-arm@0.34.5':
resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm]
os: [linux]
- libc: [glibc]
'@img/sharp-linux-ppc64@0.34.5':
resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [ppc64]
os: [linux]
- libc: [glibc]
'@img/sharp-linux-riscv64@0.34.5':
resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [riscv64]
os: [linux]
- libc: [glibc]
'@img/sharp-linux-s390x@0.33.5':
resolution: {integrity: sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [s390x]
os: [linux]
- libc: [glibc]
'@img/sharp-linux-s390x@0.34.5':
resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [s390x]
os: [linux]
- libc: [glibc]
'@img/sharp-linux-x64@0.33.5':
resolution: {integrity: sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64]
os: [linux]
- libc: [glibc]
'@img/sharp-linux-x64@0.34.5':
resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64]
os: [linux]
- libc: [glibc]
'@img/sharp-linuxmusl-arm64@0.33.5':
resolution: {integrity: sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm64]
os: [linux]
- libc: [musl]
'@img/sharp-linuxmusl-arm64@0.34.5':
resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm64]
os: [linux]
- libc: [musl]
'@img/sharp-linuxmusl-x64@0.33.5':
resolution: {integrity: sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64]
os: [linux]
- libc: [musl]
'@img/sharp-linuxmusl-x64@0.34.5':
resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64]
os: [linux]
- libc: [musl]
'@img/sharp-wasm32@0.33.5':
resolution: {integrity: sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==}
@@ -3197,56 +3169,48 @@ packages:
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
- libc: [glibc]
'@next/swc-linux-arm64-gnu@16.2.0':
resolution: {integrity: sha512-GkjL/Q7MWOwqWR9zoxu1TIHzkOI2l2BHCf7FzeQG87zPgs+6WDh+oC9Sw9ARuuL/FUk6JNCgKRkA6rEQYadUaw==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
- libc: [glibc]
'@next/swc-linux-arm64-musl@16.1.6':
resolution: {integrity: sha512-S4J2v+8tT3NIO9u2q+S0G5KdvNDjXfAv06OhfOzNDaBn5rw84DGXWndOEB7d5/x852A20sW1M56vhC/tRVbccQ==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
- libc: [musl]
'@next/swc-linux-arm64-musl@16.2.0':
resolution: {integrity: sha512-1ffhC6KY5qWLg5miMlKJp3dZbXelEfjuXt1qcp5WzSCQy36CV3y+JT7OC1WSFKizGQCDOcQbfkH/IjZP3cdRNA==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
- libc: [musl]
'@next/swc-linux-x64-gnu@16.1.6':
resolution: {integrity: sha512-2eEBDkFlMMNQnkTyPBhQOAyn2qMxyG2eE7GPH2WIDGEpEILcBPI/jdSv4t6xupSP+ot/jkfrCShLAa7+ZUPcJQ==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
- libc: [glibc]
'@next/swc-linux-x64-gnu@16.2.0':
resolution: {integrity: sha512-FmbDcZQ8yJRq93EJSL6xaE0KK/Rslraf8fj1uViGxg7K4CKBCRYSubILJPEhjSgZurpcPQq12QNOJQ0DRJl6Hg==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
- libc: [glibc]
'@next/swc-linux-x64-musl@16.1.6':
resolution: {integrity: sha512-oicJwRlyOoZXVlxmIMaTq7f8pN9QNbdes0q2FXfRsPhfCi8n8JmOZJm5oo1pwDaFbnnD421rVU409M3evFbIqg==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
- libc: [musl]
'@next/swc-linux-x64-musl@16.2.0':
resolution: {integrity: sha512-HzjIHVkmGAwRbh/vzvoBWWEbb8BBZPxBvVbDQDvzHSf3D8RP/4vjw7MNLDXFF9Q1WEzeQyEj2zdxBtVAHu5Oyw==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
- libc: [musl]
'@next/swc-win32-arm64-msvc@16.1.6':
resolution: {integrity: sha512-gQmm8izDTPgs+DCWH22kcDmuUp7NyiJgEl18bcr8irXA5N2m2O+JQIr6f3ct42GOs9c0h8QF3L5SzIxcYAAXXw==}
@@ -3406,56 +3370,48 @@ packages:
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [linux]
- libc: [glibc]
'@oxfmt/binding-linux-arm64-musl@0.43.0':
resolution: {integrity: sha512-ROaWfYh+6BSJ1Arwy5ujijTlwnZetxDxzBpDc1oBR4d7rfrPBqzeyjd5WOudowzQUgyavl2wEpzn1hw3jWcqLA==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [linux]
- libc: [musl]
'@oxfmt/binding-linux-ppc64-gnu@0.43.0':
resolution: {integrity: sha512-PJRs/uNxmFipJJ8+SyKHh7Y7VZIKQicqrrBzvfyM5CtKi8D7yZKTwUOZV3ffxmiC2e7l1SDJpkBEOyue5NAFsg==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [ppc64]
os: [linux]
- libc: [glibc]
'@oxfmt/binding-linux-riscv64-gnu@0.43.0':
resolution: {integrity: sha512-j6biGAgzIhj+EtHXlbNumvwG7XqOIdiU4KgIWRXAEj/iUbHKukKW8eXa4MIwpQwW1YkxovduKtzEAPnjlnAhVQ==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [riscv64]
os: [linux]
- libc: [glibc]
'@oxfmt/binding-linux-riscv64-musl@0.43.0':
resolution: {integrity: sha512-RYWxAcslKxvy7yri24Xm9cmD0RiANaiEPs007EFG6l9h1ChM69Q5SOzACaCoz4Z9dEplnhhneeBaTWMEdpgIbA==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [riscv64]
os: [linux]
- libc: [musl]
'@oxfmt/binding-linux-s390x-gnu@0.43.0':
resolution: {integrity: sha512-DT6Q8zfQQy3jxpezAsBACEHNUUixKSYTwdXeXojNHe4DQOoxjPdjr3Szu6BRNjxLykZM/xMNmp9ElOIyDppwtw==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [s390x]
os: [linux]
- libc: [glibc]
'@oxfmt/binding-linux-x64-gnu@0.43.0':
resolution: {integrity: sha512-R8Yk7iYcuZORXmCfFZClqbDxRZgZ9/HEidUuBNdoX8Ptx07cMePnMVJ/woB84lFIDjh2ROHVaOP40Ds3rBXFqg==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [linux]
- libc: [glibc]
'@oxfmt/binding-linux-x64-musl@0.43.0':
resolution: {integrity: sha512-F2YYqyvnQNvi320RWZNAvsaWEHwmW3k4OwNJ1hZxRKXupY63expbBaNp6jAgvYs7y/g546vuQnGHQuCBhslhLQ==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [linux]
- libc: [musl]
'@oxfmt/binding-openharmony-arm64@0.43.0':
resolution: {integrity: sha512-OE6TdietLXV3F6c7pNIhx/9YC1/2YFwjU9DPc/fbjxIX19hNIaP1rS0cFjCGJlGX+cVJwIKWe8Mos+LdQ1yAJw==}
@@ -3528,56 +3484,48 @@ packages:
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [linux]
- libc: [glibc]
'@oxlint/binding-linux-arm64-musl@1.58.0':
resolution: {integrity: sha512-zSoYRo5dxHLcUx93Stl2hW3hSNjPt99O70eRVWt5A1zwJ+FPjeCCANCD2a9R4JbHsdcl11TIQOjyigcRVOH2mw==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [linux]
- libc: [musl]
'@oxlint/binding-linux-ppc64-gnu@1.58.0':
resolution: {integrity: sha512-NQ0U/lqxH2/VxBYeAIvMNUK1y0a1bJ3ZicqkF2c6wfakbEciP9jvIE4yNzCFpZaqeIeRYaV7AVGqEO1yrfVPjA==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [ppc64]
os: [linux]
- libc: [glibc]
'@oxlint/binding-linux-riscv64-gnu@1.58.0':
resolution: {integrity: sha512-X9J+kr3gIC9FT8GuZt0ekzpNUtkBVzMVU4KiKDSlocyQuEgi3gBbXYN8UkQiV77FTusLDPsovjo95YedHr+3yg==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [riscv64]
os: [linux]
- libc: [glibc]
'@oxlint/binding-linux-riscv64-musl@1.58.0':
resolution: {integrity: sha512-CDze3pi1OO3Wvb/QsXjmLEY4XPKGM6kIo82ssNOgmcl1IdndF9VSGAE38YLhADWmOac7fjqhBw82LozuUVxD0Q==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [riscv64]
os: [linux]
- libc: [musl]
'@oxlint/binding-linux-s390x-gnu@1.58.0':
resolution: {integrity: sha512-b/89glbxFaEAcA6Uf1FvCNecBJEgcUTsV1quzrqXM/o4R1M4u+2KCVuyGCayN2UpsRWtGGLb+Ver0tBBpxaPog==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [s390x]
os: [linux]
- libc: [glibc]
'@oxlint/binding-linux-x64-gnu@1.58.0':
resolution: {integrity: sha512-0/yYpkq9VJFCEcuRlrViGj8pJUFFvNS4EkEREaN7CB1EcLXJIaVSSa5eCihwBGXtOZxhnblWgxks9juRdNQI7w==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [linux]
- libc: [glibc]
'@oxlint/binding-linux-x64-musl@1.58.0':
resolution: {integrity: sha512-hr6FNvmcAXiH+JxSvaJ4SJ1HofkdqEElXICW9sm3/Rd5eC3t7kzvmLyRAB3NngKO2wzXRCAm4Z/mGWfrsS4X8w==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [linux]
- libc: [musl]
'@oxlint/binding-openharmony-arm64@1.58.0':
resolution: {integrity: sha512-R+O368VXgRql1K6Xar+FEo7NEwfo13EibPMoTv3sesYQedRXd6m30Dh/7lZMxnrQVFfeo4EOfYIP4FpcgWQNHg==}
@@ -4711,157 +4659,131 @@ packages:
resolution: {integrity: sha512-E8jKK87uOvLrrLN28jnAAAChNq5LeCd2mGgZF+fGF5D507WlG/Noct3lP/QzQ6MrqJ5BCKNwI9ipADB6jyiq2A==}
cpu: [arm]
os: [linux]
- libc: [glibc]
'@rollup/rollup-linux-arm-gnueabihf@4.57.1':
resolution: {integrity: sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw==}
cpu: [arm]
os: [linux]
- libc: [glibc]
'@rollup/rollup-linux-arm-musleabihf@4.56.0':
resolution: {integrity: sha512-jQosa5FMYF5Z6prEpTCCmzCXz6eKr/tCBssSmQGEeozA9tkRUty/5Vx06ibaOP9RCrW1Pvb8yp3gvZhHwTDsJw==}
cpu: [arm]
os: [linux]
- libc: [musl]
'@rollup/rollup-linux-arm-musleabihf@4.57.1':
resolution: {integrity: sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw==}
cpu: [arm]
os: [linux]
- libc: [musl]
'@rollup/rollup-linux-arm64-gnu@4.56.0':
resolution: {integrity: sha512-uQVoKkrC1KGEV6udrdVahASIsaF8h7iLG0U0W+Xn14ucFwi6uS539PsAr24IEF9/FoDtzMeeJXJIBo5RkbNWvQ==}
cpu: [arm64]
os: [linux]
- libc: [glibc]
'@rollup/rollup-linux-arm64-gnu@4.57.1':
resolution: {integrity: sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g==}
cpu: [arm64]
os: [linux]
- libc: [glibc]
'@rollup/rollup-linux-arm64-musl@4.56.0':
resolution: {integrity: sha512-vLZ1yJKLxhQLFKTs42RwTwa6zkGln+bnXc8ueFGMYmBTLfNu58sl5/eXyxRa2RarTkJbXl8TKPgfS6V5ijNqEA==}
cpu: [arm64]
os: [linux]
- libc: [musl]
'@rollup/rollup-linux-arm64-musl@4.57.1':
resolution: {integrity: sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q==}
cpu: [arm64]
os: [linux]
- libc: [musl]
'@rollup/rollup-linux-loong64-gnu@4.56.0':
resolution: {integrity: sha512-FWfHOCub564kSE3xJQLLIC/hbKqHSVxy8vY75/YHHzWvbJL7aYJkdgwD/xGfUlL5UV2SB7otapLrcCj2xnF1dg==}
cpu: [loong64]
os: [linux]
- libc: [glibc]
'@rollup/rollup-linux-loong64-gnu@4.57.1':
resolution: {integrity: sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA==}
cpu: [loong64]
os: [linux]
- libc: [glibc]
'@rollup/rollup-linux-loong64-musl@4.56.0':
resolution: {integrity: sha512-z1EkujxIh7nbrKL1lmIpqFTc/sr0u8Uk0zK/qIEFldbt6EDKWFk/pxFq3gYj4Bjn3aa9eEhYRlL3H8ZbPT1xvA==}
cpu: [loong64]
os: [linux]
- libc: [musl]
'@rollup/rollup-linux-loong64-musl@4.57.1':
resolution: {integrity: sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw==}
cpu: [loong64]
os: [linux]
- libc: [musl]
'@rollup/rollup-linux-ppc64-gnu@4.56.0':
resolution: {integrity: sha512-iNFTluqgdoQC7AIE8Q34R3AuPrJGJirj5wMUErxj22deOcY7XwZRaqYmB6ZKFHoVGqRcRd0mqO+845jAibKCkw==}
cpu: [ppc64]
os: [linux]
- libc: [glibc]
'@rollup/rollup-linux-ppc64-gnu@4.57.1':
resolution: {integrity: sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w==}
cpu: [ppc64]
os: [linux]
- libc: [glibc]
'@rollup/rollup-linux-ppc64-musl@4.56.0':
resolution: {integrity: sha512-MtMeFVlD2LIKjp2sE2xM2slq3Zxf9zwVuw0jemsxvh1QOpHSsSzfNOTH9uYW9i1MXFxUSMmLpeVeUzoNOKBaWg==}
cpu: [ppc64]
os: [linux]
- libc: [musl]
'@rollup/rollup-linux-ppc64-musl@4.57.1':
resolution: {integrity: sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw==}
cpu: [ppc64]
os: [linux]
- libc: [musl]
'@rollup/rollup-linux-riscv64-gnu@4.56.0':
resolution: {integrity: sha512-in+v6wiHdzzVhYKXIk5U74dEZHdKN9KH0Q4ANHOTvyXPG41bajYRsy7a8TPKbYPl34hU7PP7hMVHRvv/5aCSew==}
cpu: [riscv64]
os: [linux]
- libc: [glibc]
'@rollup/rollup-linux-riscv64-gnu@4.57.1':
resolution: {integrity: sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A==}
cpu: [riscv64]
os: [linux]
- libc: [glibc]
'@rollup/rollup-linux-riscv64-musl@4.56.0':
resolution: {integrity: sha512-yni2raKHB8m9NQpI9fPVwN754mn6dHQSbDTwxdr9SE0ks38DTjLMMBjrwvB5+mXrX+C0npX0CVeCUcvvvD8CNQ==}
cpu: [riscv64]
os: [linux]
- libc: [musl]
'@rollup/rollup-linux-riscv64-musl@4.57.1':
resolution: {integrity: sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw==}
cpu: [riscv64]
os: [linux]
- libc: [musl]
'@rollup/rollup-linux-s390x-gnu@4.56.0':
resolution: {integrity: sha512-zhLLJx9nQPu7wezbxt2ut+CI4YlXi68ndEve16tPc/iwoylWS9B3FxpLS2PkmfYgDQtosah07Mj9E0khc3Y+vQ==}
cpu: [s390x]
os: [linux]
- libc: [glibc]
'@rollup/rollup-linux-s390x-gnu@4.57.1':
resolution: {integrity: sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg==}
cpu: [s390x]
os: [linux]
- libc: [glibc]
'@rollup/rollup-linux-x64-gnu@4.56.0':
resolution: {integrity: sha512-MVC6UDp16ZSH7x4rtuJPAEoE1RwS8N4oK9DLHy3FTEdFoUTCFVzMfJl/BVJ330C+hx8FfprA5Wqx4FhZXkj2Kw==}
cpu: [x64]
os: [linux]
- libc: [glibc]
'@rollup/rollup-linux-x64-gnu@4.57.1':
resolution: {integrity: sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg==}
cpu: [x64]
os: [linux]
- libc: [glibc]
'@rollup/rollup-linux-x64-musl@4.56.0':
resolution: {integrity: sha512-ZhGH1eA4Qv0lxaV00azCIS1ChedK0V32952Md3FtnxSqZTBTd6tgil4nZT5cU8B+SIw3PFYkvyR4FKo2oyZIHA==}
cpu: [x64]
os: [linux]
- libc: [musl]
'@rollup/rollup-linux-x64-musl@4.57.1':
resolution: {integrity: sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw==}
cpu: [x64]
os: [linux]
- libc: [musl]
'@rollup/rollup-openbsd-x64@4.56.0':
resolution: {integrity: sha512-O16XcmyDeFI9879pEcmtWvD/2nyxR9mF7Gs44lf1vGGx8Vg2DRNx11aVXBEqOQhWb92WN4z7fW/q4+2NYzCbBA==}
@@ -5335,28 +5257,24 @@ packages:
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
- libc: [glibc]
'@tailwindcss/oxide-linux-arm64-musl@4.1.18':
resolution: {integrity: sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
- libc: [musl]
'@tailwindcss/oxide-linux-x64-gnu@4.1.18':
resolution: {integrity: sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
- libc: [glibc]
'@tailwindcss/oxide-linux-x64-musl@4.1.18':
resolution: {integrity: sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
- libc: [musl]
'@tailwindcss/oxide-wasm32-wasi@4.1.18':
resolution: {integrity: sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA==}
@@ -6077,7 +5995,7 @@ packages:
basic-ftp@5.1.0:
resolution: {integrity: sha512-RkaJzeJKDbaDWTIPiJwubyljaEPwpVWkm9Rt5h9Nd6h7tEXTJ3VB4qxdZBioV7JO5yLUaOKwz7vDOzlncUsegw==}
engines: {node: '>=10.0.0'}
- deprecated: Security vulnerability fixed in 5.2.1, please upgrade
+ deprecated: Security vulnerability fixed in 5.2.0, please upgrade
bcp-47-match@2.0.3:
resolution: {integrity: sha512-JtTezzbAibu8G0R9op9zb3vcWZd9JF6M0xOYGPn0fNCd7wOpRB1mU2mH9T8gaBGbAAyIIVgB2G7xG0GP98zMAQ==}
@@ -8074,28 +7992,24 @@ packages:
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [linux]
- libc: [glibc]
lightningcss-linux-arm64-musl@1.30.2:
resolution: {integrity: sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==}
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [linux]
- libc: [musl]
lightningcss-linux-x64-gnu@1.30.2:
resolution: {integrity: sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [linux]
- libc: [glibc]
lightningcss-linux-x64-musl@1.30.2:
resolution: {integrity: sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [linux]
- libc: [musl]
lightningcss-win32-arm64-msvc@1.30.2:
resolution: {integrity: sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==}