Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,8 @@
"**/.turbo": true,
"**/node_modules": true,
"**/pnpm-lock.yaml": true
},
"[typescript]": {
"editor.defaultFormatter": "oxc.oxc-vscode"
}
}
4 changes: 4 additions & 0 deletions apps/cms/schemas/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -38,4 +40,6 @@ export const schemaTypes = [
time,
merch,
hungerGames,
trophies,
trophy,
];
55 changes: 55 additions & 0 deletions apps/cms/schemas/objects/trophy.tsx
Original file line number Diff line number Diff line change
@@ -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,
};
},
},
});
70 changes: 70 additions & 0 deletions apps/cms/schemas/trophies.tsx
Original file line number Diff line number Diff line change
@@ -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,
};
},
},
});
2 changes: 2 additions & 0 deletions apps/uno/bootstrap/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{
Expand Down Expand Up @@ -130,6 +131,7 @@ func RunApi() {
cmsMeetingMinuteRepo,
cmsMovieRepo,
cmsHSApplicationRepo,
cmsTrophyRepo,
cacheInvalidator,
)

Expand Down
18 changes: 18 additions & 0 deletions apps/uno/domain/model/cms_trophy.go
Original file line number Diff line number Diff line change
@@ -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"`
}
4 changes: 4 additions & 0 deletions apps/uno/domain/port/cms.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
8 changes: 8 additions & 0 deletions apps/uno/domain/service/cms.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ var cmsTypeNamespaces = map[string][]string{
"merch": {sanityinfra.CMSMerchNamespaceMerch},
"meetingMinute": {sanityinfra.CMSMeetingMinuteNamespaceMeetingMinutes},
"movie": {sanityinfra.CMSMovieNamespaceMovies},
"trophy": {sanityinfra.CMSTrophyNamespaceTrophies},
}

type CMSService struct {
Expand All @@ -36,6 +37,7 @@ type CMSService struct {
meetingMinuteRepo port.CMSMeetingMinuteRepo
movieRepo port.CMSMovieRepo
hsApplicationRepo port.CMSHSApplicationRepo
trophyRepo port.CMSTrophyRepo

invalidator port.CacheInvalidator
}
Expand All @@ -52,6 +54,7 @@ func NewCMSService(
meetingMinuteRepo port.CMSMeetingMinuteRepo,
movieRepo port.CMSMovieRepo,
hsApplicationRepo port.CMSHSApplicationRepo,
trophyRepo port.CMSTrophyRepo,
invalidator port.CacheInvalidator,
) *CMSService {
return &CMSService{
Expand All @@ -65,6 +68,7 @@ func NewCMSService(
merchRepo: merchRepo,
meetingMinuteRepo: meetingMinuteRepo,
movieRepo: movieRepo,
trophyRepo: trophyRepo,
hsApplicationRepo: hsApplicationRepo,

invalidator: invalidator,
Expand Down Expand Up @@ -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
Expand Down
23 changes: 23 additions & 0 deletions apps/uno/http/routes/api/sanity_cms.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ func NewSanityMux(

mux.GET("/hs-applications", s.getAllHSApplications)

mux.GET("/trophies", s.getAllTrophies)

return mux
}

Expand Down Expand Up @@ -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
Expand Down
94 changes: 94 additions & 0 deletions apps/uno/infrastructure/external/sanity/trophy_repo.go
Original file line number Diff line number Diff line change
@@ -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
}
Loading
Loading