From 34c249254d069b2a8e9b366ccae302868ababf18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linn=C3=A9a?= Date: Fri, 26 Sep 2025 12:39:22 +0200 Subject: [PATCH 1/7] Add positions page --- prisma/schema.prisma | 1 + src/app/positions/page.tsx | 106 ++++++++++++++++ src/components/corps/infobox.tsx | 2 +- src/components/corps/position-infobox.tsx | 140 ++++++++++++++++++++++ 4 files changed, 248 insertions(+), 1 deletion(-) create mode 100644 src/app/positions/page.tsx create mode 100644 src/components/corps/position-infobox.tsx diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 28ca3edb..18facda4 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -78,6 +78,7 @@ model Corps { language String @default("sv") createdAt DateTime @default(now()) updatedAt DateTime @updatedAt + contactUrl String? roles Role[] permissions Permission[] diff --git a/src/app/positions/page.tsx b/src/app/positions/page.tsx new file mode 100644 index 00000000..f21ef753 --- /dev/null +++ b/src/app/positions/page.tsx @@ -0,0 +1,106 @@ +import { lang } from 'utils/language'; +import PositionInfobox from 'components/corps/position-infobox'; +import { api } from 'trpc/server'; +import { IconMail } from '@tabler/icons-react'; +import ActionIcon from 'components/input/action-icon'; + + +const styrelseOrder: Dictionary = { + "Ordförande": 1, + "ViceOrdförande": 2, + "Sekreterare": 3, + "Kassör": 4, +}; + + +const Positions = async () => { + const roles = await api.permission.getRoles.query(); + const ordoredBoardCorps = await Promise.all( + roles + .filter(role => Object.keys(styrelseOrder).includes(role.name)) + .sort((a, b) => styrelseOrder[a.name] - styrelseOrder[b.name]) + .flatMap(role => + role.corpsii.map(async (corps) => { + const result = await api.corps.get.query({ id: corps.id }); + if (!result) throw new Error("Corps not found"); + return result; + }) + ) + ); + + const TrivselCorps = await Promise.all( + roles + .filter(role => role.name === "Trivselombud") + .flatMap(role => + role.corpsii.map(async (corps) => { + const result = await api.corps.get.query({ id: corps.id }); + if (!result) throw new Error("Corps not found"); + return result; + }) + ) + ); + + return ( +
+

{lang('Ansvarsposter', 'Positions of responsibility')}

+
+
+
+

styrelsen

+ bla bla lite text +
+ {ordoredBoardCorps.map((corps) => ( +
+ +
+ )) + } +
+
+
+
+ +
+
+

Trivselombud

+ bla bla mer text. Vi behöver bättre info rutor. + {roles.filter((role) => ( + role.name == "Trivselombud" + )).map((role) => ( +
+ {TrivselCorps.map((corps) => ( +
+ +
+ )) + } +
+ ))} +
+
+
+
+

Utskott

+ bla bla mer text. +
+

Pryl och Prov

+ + + +
+ blub + + +

PR

+ blab +
+
+
+ ); +}; + +export default Positions; + diff --git a/src/components/corps/infobox.tsx b/src/components/corps/infobox.tsx index a537b39e..38106839 100644 --- a/src/components/corps/infobox.tsx +++ b/src/components/corps/infobox.tsx @@ -86,7 +86,7 @@ const CorpsInfobox = ({ id, open }: CorpsInfoboxProps) => { const joinedAt = (firstGigDate?.getTime() ?? Number.MAX_VALUE) < - (firstRehearsalDate?.getTime() ?? Number.MAX_VALUE) + (firstRehearsalDate?.getTime() ?? Number.MAX_VALUE) ? firstGigDate : firstRehearsalDate; diff --git a/src/components/corps/position-infobox.tsx b/src/components/corps/position-infobox.tsx new file mode 100644 index 00000000..6095da64 --- /dev/null +++ b/src/components/corps/position-infobox.tsx @@ -0,0 +1,140 @@ +import { IconMail } from '@tabler/icons-react'; +import ActionIcon from 'components/input/action-icon'; +import { filterNone } from 'utils/array'; +import { numberAndFullName } from 'utils/corps'; +import { lang } from 'utils/language'; + +interface Instrument { + instrument: { name: string }; + isMainInstrument: boolean; +} + +interface Corps { + id: string; + firstName: string; + lastName: string; + nickName: string | null; + pronouns: string | null; + number: number | null; + bNumber: number | null; + contactUrl: string | null; + points: number; + firstGigDate: Date | undefined; + firstRehearsalDate: Date | undefined; + instruments: Instrument[]; +} + +interface CorpsInfoboxProps { + corps: Corps; +} + +const genOtherInstrumentsString = (instruments: string[]) => { + const instrumentsLower = instruments.map((i) => i.toLowerCase()); + if (instrumentsLower.length === 0) return ''; + if (instrumentsLower.length === 1) return instrumentsLower[0] ?? ''; + return `${instrumentsLower + .slice(0, instrumentsLower.length - 1) + .join(', ')} och ${instrumentsLower[instrumentsLower.length - 1] ?? ''}`; +}; + +// A list of "instruments" which should have the prefix "är" +const beingPrefixes = ['dirigent', 'balett', 'slagverksfröken']; + + +const PositionInfobox = ({ corps }: CorpsInfoboxProps) => { + const corpsNameTemp = numberAndFullName(corps); + const corpsName = + corpsNameTemp.length > 25 + ? corpsNameTemp.slice(0, 25) + corpsNameTemp.slice(25).replace(' ', '\n') + : corpsNameTemp; + + const { + nickName, + pronouns, + contactUrl, + points, + firstGigDate, + firstRehearsalDate, + instruments + } = corps; + + const mainInstrument = + instruments.find((i) => i.isMainInstrument)?.instrument.name ?? ''; + const otherInstruments = instruments + .filter((i) => !i.isMainInstrument) + .map((i) => i.instrument.name); + + const isPlayingMainInstrument = !beingPrefixes.includes( + mainInstrument.toLowerCase(), + ); + const isPlayingOtherInstrument = !otherInstruments.some((i) => + beingPrefixes.includes(i.toLowerCase()), + ); + + + const joinedAt = + (firstGigDate?.getTime() ?? Number.MAX_VALUE) < + (firstRehearsalDate?.getTime() ?? Number.MAX_VALUE) + ? firstGigDate + : firstRehearsalDate; + + const joinedMsg = `Gick med i corpset den ${joinedAt?.getDate()} ${joinedAt?.toLocaleDateString( + 'sv', + { month: 'long' }, + )} ${joinedAt?.getFullYear()}.`; + + const temp1 = isPlayingMainInstrument ? 'Spelar ' : 'Är '; + + // If the main instrument is the same as the other instruments, we don't need to specify it twice + const temp2 = + isPlayingMainInstrument !== isPlayingOtherInstrument + ? isPlayingOtherInstrument + ? 'spelar ' + : 'är ' + : ''; + + const instrumentsMsg = + temp1 + + (otherInstruments.length > 0 ? 'främst ' : '') + + mainInstrument.toLowerCase() + + (otherInstruments.length > 0 + ? ', men ' + temp2 + 'även ' + genOtherInstrumentsString(otherInstruments) + : '') + + '.'; + + return ( +
+
+
+ {corpsName} + + {(contactUrl) && ( + + + + )} +
+ {(nickName || pronouns) && ( +
+ {filterNone([corps.nickName, corps.pronouns]).join(' • ')} +
+ )} +
+
+ {lang('Spelpoäng: ', 'Gig points: ')} + {points} +
+
+
+ {joinedAt && joinedMsg} {instrumentsMsg}{' '} + +
+ +
+ ); +}; + +export default PositionInfobox; From 76140679e817d702c07a1959fc0d194f22c65446 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linn=C3=A9a?= Date: Thu, 30 Oct 2025 20:52:13 +0100 Subject: [PATCH 2/7] Make contactURL upatable by trivselombud Rename to contactURL --- prisma/schema.prisma | 2 +- src/app/account/preferences.tsx | 12 ++++++++++++ src/components/corps-form.tsx | 3 +++ src/components/corps/position-infobox.tsx | 8 ++++---- src/server/trpc/router/corps.ts | 6 ++++++ 5 files changed, 26 insertions(+), 5 deletions(-) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 18facda4..238761a7 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -78,7 +78,7 @@ model Corps { language String @default("sv") createdAt DateTime @default(now()) updatedAt DateTime @updatedAt - contactUrl String? + contactURL String? roles Role[] permissions Permission[] diff --git a/src/app/account/preferences.tsx b/src/app/account/preferences.tsx index 8a1691b4..484e6a18 100644 --- a/src/app/account/preferences.tsx +++ b/src/app/account/preferences.tsx @@ -24,6 +24,7 @@ const initialValues = { lactoseFree: false, otherFoodPrefs: '', email: '', + contactURL: '', mainInstrument: '', }; type FormValues = typeof initialValues; @@ -59,6 +60,7 @@ const AccountPreferences = () => { lactoseFree: corps.foodPrefs?.lactoseFree ?? false, otherFoodPrefs: corps.foodPrefs?.other ?? '', email: corps.user.email || undefined, + contactURL: corps.contactURL ?? '', mainInstrument, }); }, [corps]); @@ -86,6 +88,8 @@ const AccountPreferences = () => { label: i.instrument.name, })); + const isTrivselOmbud = corps?.roles.some(role => role.name === "Trivselombud"); + return (
@@ -155,6 +159,14 @@ const AccountPreferences = () => { label={lang('Pronomen', 'Pronouns')} {...form.getInputProps('pronouns')} /> + { + (isTrivselOmbud) && + () + } +

{lang('Matpreferenser', 'Food preferences')}

diff --git a/src/components/corps-form.tsx b/src/components/corps-form.tsx index ad171b2b..e42dd03f 100644 --- a/src/components/corps-form.tsx +++ b/src/components/corps-form.tsx @@ -19,6 +19,7 @@ const initialValues = { number: '', bNumber: '', email: '', + contactURL: '', mainInstrument: '', otherInstruments: [] as string[], roles: [] as Permission[], @@ -78,6 +79,7 @@ const CorpsForm = ({ corpsId }: AdminCorpsProps) => { number: corps.number?.toString() || '', bNumber: corps.bNumber?.toString() || '', email: corps.user.email ?? '', + contactURL: corps.contactURL ?? '', mainInstrument, otherInstruments, roles: corps.roles.map((r) => r.name as Permission), @@ -133,6 +135,7 @@ const CorpsForm = ({ corpsId }: AdminCorpsProps) => {
+
diff --git a/src/components/corps/position-infobox.tsx b/src/components/corps/position-infobox.tsx index 6095da64..26a2babb 100644 --- a/src/components/corps/position-infobox.tsx +++ b/src/components/corps/position-infobox.tsx @@ -17,7 +17,7 @@ interface Corps { pronouns: string | null; number: number | null; bNumber: number | null; - contactUrl: string | null; + contactURL: string | null; points: number; firstGigDate: Date | undefined; firstRehearsalDate: Date | undefined; @@ -51,7 +51,7 @@ const PositionInfobox = ({ corps }: CorpsInfoboxProps) => { const { nickName, pronouns, - contactUrl, + contactURL, points, firstGigDate, firstRehearsalDate, @@ -108,9 +108,9 @@ const PositionInfobox = ({ corps }: CorpsInfoboxProps) => {
{corpsName} - {(contactUrl) && ( + {(contactURL) && ( diff --git a/src/server/trpc/router/corps.ts b/src/server/trpc/router/corps.ts index 1e641bd8..8de11b88 100644 --- a/src/server/trpc/router/corps.ts +++ b/src/server/trpc/router/corps.ts @@ -141,6 +141,7 @@ export const corpsRouter = router({ nickName: z.string().transform(emptyToNull), pronouns: z.string().transform(emptyToNull), email: z.string(), + contactURL: z.string(), vegetarian: z.boolean(), vegan: z.boolean(), glutenFree: z.boolean(), @@ -154,6 +155,7 @@ export const corpsRouter = router({ nickName, pronouns, email, + contactURL, vegetarian, vegan, glutenFree, @@ -217,6 +219,7 @@ export const corpsRouter = router({ data: { nickName, pronouns, + contactURL, user: { update: { email: email.trim(), @@ -243,6 +246,7 @@ export const corpsRouter = router({ number: z.number().nullable(), bNumber: z.number().nullable(), email: z.string(), + contactURL: z.string(), mainInstrument: z.string(), otherInstruments: z.array(z.string()), roles: z.array(z.string()), @@ -261,6 +265,8 @@ export const corpsRouter = router({ input.nickName.trim().length > 0 ? input.nickName.trim() : null, pronouns: input.pronouns.trim().length > 0 ? input.pronouns.trim() : null, + contactURL: + input.contactURL.trim().length > 0 ? input.contactURL.trim() : null, number: input.number, bNumber: input.bNumber, instruments: { From 351a8031656f81eef5e49cc0c9ccfe892ade422f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linn=C3=A9a?= Date: Fri, 31 Oct 2025 11:28:51 +0100 Subject: [PATCH 3/7] small updates --- src/app/positions/page.tsx | 25 +++++++++++++++++++---- src/components/corps/position-infobox.tsx | 2 +- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/src/app/positions/page.tsx b/src/app/positions/page.tsx index f21ef753..92c11b6a 100644 --- a/src/app/positions/page.tsx +++ b/src/app/positions/page.tsx @@ -46,11 +46,12 @@ const Positions = async () => {
-

styrelsen

+

{lang('styrelsen', 'The Board')}

bla bla lite text
{ordoredBoardCorps.map((corps) => ( -
+
+ {corps.roles.filter(role => Object.keys(styrelseOrder).includes(role.name))[0]?.name}
)) @@ -63,13 +64,13 @@ const Positions = async () => {

Trivselombud

- bla bla mer text. Vi behöver bättre info rutor. + bla bla mer text. {roles.filter((role) => ( role.name == "Trivselombud" )).map((role) => (
{TrivselCorps.map((corps) => ( -
+
)) @@ -78,6 +79,7 @@ const Positions = async () => { ))}
+

Utskott

@@ -96,6 +98,21 @@ const Positions = async () => {

PR

blab + +
+

ITK

+ + + +
+ {lang(`ITK har ansvar för drift av alla Bleckhornens hemsidor, samt vidareutveckling av Blindtarmen. + Driftansvaret includerar blindtarmen, den publika hemsidan och vår interna wiki.`, + `ITK has responsibility for the operation of all Bleckhornens websites, as well as developing Blindtarmen. + The operational responsebility includes Blindtarmen, the public website, and our internal wiki`)} +
diff --git a/src/components/corps/position-infobox.tsx b/src/components/corps/position-infobox.tsx index 26a2babb..7a77dc28 100644 --- a/src/components/corps/position-infobox.tsx +++ b/src/components/corps/position-infobox.tsx @@ -103,7 +103,7 @@ const PositionInfobox = ({ corps }: CorpsInfoboxProps) => { '.'; return ( -
+
{corpsName} From dda31844834bd5e2dae6a43e90ee346bffb8b490 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linn=C3=A9a?= Date: Fri, 31 Oct 2025 11:49:42 +0100 Subject: [PATCH 4/7] Add roleEmails --- src/components/corps/position-infobox.tsx | 25 +++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/src/components/corps/position-infobox.tsx b/src/components/corps/position-infobox.tsx index 7a77dc28..55571601 100644 --- a/src/components/corps/position-infobox.tsx +++ b/src/components/corps/position-infobox.tsx @@ -9,6 +9,10 @@ interface Instrument { isMainInstrument: boolean; } +interface Role { + name: string; +} + interface Corps { id: string; firstName: string; @@ -22,6 +26,7 @@ interface Corps { firstGigDate: Date | undefined; firstRehearsalDate: Date | undefined; instruments: Instrument[]; + roles: Role[]; } interface CorpsInfoboxProps { @@ -37,6 +42,19 @@ const genOtherInstrumentsString = (instruments: string[]) => { .join(', ')} och ${instrumentsLower[instrumentsLower.length - 1] ?? ''}`; }; +const roleToEmail: Record = { + Ordförande: "ordforande@bleckhornen.org", + ViceOrdförande: "vice@bleckhornen.org", + Sekreterare: "sekreterare@bleckhornen.org", + Kassör: "kassor@bleckhornen.org", +}; + +const roleListToEmail = (roles: Role[]) => { + const matchingRole = roles.find(r => roleToEmail[r.name]); + + return matchingRole ? ('mailto:' + roleToEmail[matchingRole.name]) : null; +} + // A list of "instruments" which should have the prefix "är" const beingPrefixes = ['dirigent', 'balett', 'slagverksfröken']; @@ -102,15 +120,18 @@ const PositionInfobox = ({ corps }: CorpsInfoboxProps) => { : '') + '.'; + const roleEmail = roleListToEmail(corps.roles) + const contact = roleEmail ? roleEmail : contactURL + return (
{corpsName} - {(contactURL) && ( + {(contact) && ( From b8b79a713824e6cac8d3710fccda53bc2f6c6715 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linn=C3=A9a?= Date: Fri, 31 Oct 2025 12:11:17 +0100 Subject: [PATCH 5/7] change divs a bit --- src/app/positions/page.tsx | 45 ++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/src/app/positions/page.tsx b/src/app/positions/page.tsx index 92c11b6a..d3ed8a48 100644 --- a/src/app/positions/page.tsx +++ b/src/app/positions/page.tsx @@ -43,25 +43,23 @@ const Positions = async () => { return (

{lang('Ansvarsposter', 'Positions of responsibility')}

-
-
-
-

{lang('styrelsen', 'The Board')}

- bla bla lite text -
- {ordoredBoardCorps.map((corps) => ( -
- {corps.roles.filter(role => Object.keys(styrelseOrder).includes(role.name))[0]?.name} - -
- )) - } -
+
+
+

{lang('Styrelsen', 'The Board')}

+ bla bla lite text +
+ {ordoredBoardCorps.map((corps) => ( +
+ {corps.roles.filter(role => Object.keys(styrelseOrder).includes(role.name))[0]?.name} + +
+ )) + }
-
+

Trivselombud

bla bla mer text. @@ -80,10 +78,11 @@ const Positions = async () => {
-
+

Utskott

bla bla mer text. +

Pryl och Prov

{
blub - -

PR

- blab +
+

PR

+ + + +
+ blub

ITK

@@ -113,6 +119,7 @@ const Positions = async () => { `ITK has responsibility for the operation of all Bleckhornens websites, as well as developing Blindtarmen. The operational responsebility includes Blindtarmen, the public website, and our internal wiki`)} +
From ed682b82c1600fb50fc9896b3fd4f6510822500c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linn=C3=A9a?= Date: Sun, 9 Nov 2025 18:01:00 +0100 Subject: [PATCH 6/7] I'm done --- src/app/info/page.tsx | 305 ++++++++++++++++++++++++++++++++ src/app/positions/page.tsx | 130 -------------- src/components/navbar/index.tsx | 2 + 3 files changed, 307 insertions(+), 130 deletions(-) create mode 100644 src/app/info/page.tsx delete mode 100644 src/app/positions/page.tsx diff --git a/src/app/info/page.tsx b/src/app/info/page.tsx new file mode 100644 index 00000000..44f6ad01 --- /dev/null +++ b/src/app/info/page.tsx @@ -0,0 +1,305 @@ +import { lang } from 'utils/language'; +import PositionInfobox from 'components/corps/position-infobox'; +import { api } from 'trpc/server'; +import { IconMail } from '@tabler/icons-react'; +import ActionIcon from 'components/input/action-icon'; + + +const styrelseOrder: Dictionary = { + "Ordförande": 1, + "ViceOrdförande": 2, + "Sekreterare": 3, + "Kassör": 4, +}; + + +const Positions = async () => { + const roles = await api.permission.getRoles.query(); + const ordoredBoardCorps = await Promise.all( + roles + .filter(role => Object.keys(styrelseOrder).includes(role.name)) + .sort((a, b) => styrelseOrder[a.name] - styrelseOrder[b.name]) + .flatMap(role => + role.corpsii.map(async (corps) => { + const result = await api.corps.get.query({ id: corps.id }); + if (!result) throw new Error("Corps not found"); + return result; + }) + ) + ); + + const TrivselCorps = await Promise.all( + roles + .filter(role => role.name === "Trivselombud") + .flatMap(role => + role.corpsii.map(async (corps) => { + const result = await api.corps.get.query({ id: corps.id }); + if (!result) throw new Error("Corps not found"); + return result; + }) + ) + ); + + return ( +
+

{lang('Ansvarsposter', 'Positions of responsibility')}

+
+
+

{lang('Styrelsen', 'The Board')}

+ {lang(`Styrelsen leder Bleckhornen under ett verksamhetsår. Tillsammans med dirigenter, + balettledare, utskottsmedlemmar och andra förtroendevalda ansvarar styrelsen för planering + av repor, spelningar, sittningar, konserter, resor och andra aktiviteter. Detta görs med stöd och + samarbete med hela corpset`, + + `The board leads Bleckhornen throughout the year. Together with conductors, ballet leaders, + committee members, and other elected representatives, the board is responsible for planning + rehearsals, performances, formal sittings, concerts, trips, and other activities. This is done + with the support and collaboration of the entire corps.`)} +
+ {ordoredBoardCorps.map((corps) => ( +
+ {corps.roles.filter(role => Object.keys(styrelseOrder).includes(role.name))[0]?.name} + +
+ )) + } +
+
+
+ +
+
+

Trivselombud

+ {lang(`Trivselombuden i Bleckhornen har i uppdrag att bidra till trivsel och trygghet inom + föreningen. Till oss kan du komma om du känner att du har upplevt något inom Bleckhornen + som känns fel, eller om du känner att du behöver stöd eller prata om något. Trivselombuden + kan du alltid komma pch prata med när du känner för det, men du kan också kontakta oss via + våra formulär, där det även finns möjlighet att vara anonym.`, + + `The Wellbeing Representatives in Bleckhornen are responsible for promoting comfort and + safety within the association. You can come to us if you've experienced something within + Bleckhornen that feels wrong, or if you feel that you need support or just someone to talk to. + You can always approach the Wellbeing Representatives whenever you feel the need, but you + can also contact us through our forms, where there is also an option to remain anonymous.`)} + {roles.filter((role) => ( + role.name == "Trivselombud" + )).map((role) => ( +
+ {TrivselCorps.map((corps) => ( +
+ +
+ )) + } +
+ ))} +
+
+ +
+
+

Utskott

+ {lang(`I Bleckhornen finns flera utskott, där varje utskott har sina specifika uppgifter. + Tillsammans ser de till att föreningen fungerar smidigt och utvecklas. + Det finns ett utskott för alla, där din kreativitet och personlighet får flöda. + Samtidigt får du även chansen att lära känna personer från olika sektioner.`, + + `In Bleckhornen, there are several committees, each with its own specific tasks. + Together, they ensure that the association runs smoothly and continues to develop. + There’s a committee for everyone, where your creativity and personality can shine. + At the same time, you also get the chance to meet and get to know people from different sections.`)} + +
+

Notmarskeriet

+ + + +
+ {lang(`Notmarskeriet ansvarar för alla Bleckhornens noter. Detta innebär att vi skriver ut 📠, sätter + in 📒, och arkiverar ️ alla de noter som finns i våra pärmar och häften! Arbetsbördan som + notmarsk är generellt koncentrerad runt julkonserten och karnevalerna, och då kan den vara + rätt stor, men under den tidiga hösten och samt så gott som hela våren de år Lund ej gästas av + karneval är oftast relativt lite att göra.`, + + `The "Notmarskeri" (roughly "Note Marshallery") is responsible for Bleckhornen's sheet + music. This means that we print 📠, insert 📒, and archive ️ all the sheet music that can be + found in our folders and booklets! A note marshal's workload is typically concentrated + around the Christmas concert and the Karnevals, and at those points it can be a bit, but early + Autumn and basically all of the spring semester (on non-karneval years) are typically very + free`)} + +
+

Arkivet

+ + + +
+ {lang(`Arkivet är utskottet som förevigar allt skoj! Vi ser till att spara minnen från det roliga vi gör, + bland annat i form av bilder, filmer och affischer som corps har producerat. Vi sparar också + resultat av det slit andra funktionärer lägger ner, som corpset skulle kunna behöva i + framtiden.`, + + `The Archive is the committee that immortalizes all the fun! We make sure to preserve + memories from all the enjoyable things we do. For example, in the form of photos, videos, + and posters produced by the corps. We also keep the results of the hard work put in by other + functionaries, which the corps might need in the future.`)} + +
+

PR

+ + + +
+ {lang(`PR är utskottet som ser till att vi syns och hörs även utöver spelningarna! Vårt arbete är + främst att sköta våra sociala medier och fota på spelningar. Inför julkonserten gör vi även + affischerna, programbladen och söker spons. I detta utskott får kreativa idéer komma till liv!`, + + `PR is the committee that makes sure we’re seen and heard even beyond our performances! + Our work mainly involves managing our social media and taking photos at gigs. Before the + Christmas concert, we also create the posters and programs and handle sponsorships. In this + committee, creative ideas come to life!`)} + +
+

Baren

+ + + +
+ {lang(`Det är baren som förser corpset med grogg! (Ber du snällt kanske du till och med kan få en drink 😉) + Den första torsdagen varje månad bjuder vi in till extra festlig eftersits med extra festlig dryck! + Vi serverar dessutom fördrink inför corpsafton, och rattar Bussbaren hela vägen upp till SOF och STORK. + Eins, zwei, drei, gesoffa!`, + + `The Bar is the committee that keeps the corps supplied with drinks! (If you ask nicely, you might even get a cocktail 😉) + On the first Thursday of every month, we host an extra festive afterparty with extra festive beverages! + We also serve pre-drinks before corps evenings and run the Bus Bar all the way to SOF and STORK.`)} + +
+

Sexmästeriet

+ +
+ {lang(`Sexmästeriet är utskottet som ser till att ingen går hungrig! Vi lagar mat inför både Vårcorps och Höstcorps, + och ser till att hela corpset får njuta av god mat. + Tillsammans handlar vi ingredienser, lagar maten och har det riktigt roligt! + Som medlem i Sexmästeriet får du också vara med och bestämma vad som ska lagas (och presentera maten under middagarna).`, + + `The Culinary Committee makes sure no one goes hungry! + We cook for both Vårcorps and Höstcorps, making sure the whole corps gets to enjoy tasty food. + Together, we shop for ingredients, cook, and have a lot of fun along the way! + As a member, you also get to help decide what’s on the menu (and show off your creations at the dinners).`)} + +
+

ITK

+ + + +
+ {lang(`ITK har ansvar för drift av alla Bleckhornens hemsidor, samt vidareutveckling av Blindtarmen. + Driftansvaret includerar blindtarmen, den publika hemsidan och vår interna wiki.`, + + `ITK has responsibility for the operation of all Bleckhornens websites, as well as developing Blindtarmen. + The operational responsebility includes Blindtarmen, the public website, and our internal wiki`)} + +
+

Pryl & prov

+ + + +
+ {lang(`I pryl & prov har vi ansvar för corpsets merch och provelever! + Vi försöker se till att proveleverna känner sig välkomna i föreningen + och att de alltid har någon att rikta frågor till om föreningen. + Detta gör vi genom att anordna tillställningar som t.ex. provelevsfördrinkar och en provelevsdag! + När det kommer till merchen köper vi in och säljer föreningens merch, + och ibland när vi får feeling designar vi också ny merch!!`, + + `In Pryl & Prov, we’re responsible for the corps’ merch and for the newmembers! + We make sure that new members feel welcome in Bleckhornen + and that they always have someone to turn to with questions about how things work. + We do this by organizing events such as pre-drinks for the new members and a special probationary members’ day! + When it comes to merch, we handle the purchasing and sales of the orchestra's merchandise and sometimes, + when we’re feeling inspired, we even design new merch ourselves!`)} + +
+

Materialförvaltarna

+ + + +
+ {lang(`Materialförvaltarna tar hand om och utvecklar Tarmen och ser till Bleckhornens prylar fungerar. + Har du ett roligt projekt du skulle vilja genomföra kan du alltid dryfta din idé med oss för att få tips och stöd.`, + + `The materials managers take care of and develop Tarmen and make sure the Bleckhorns’ equipment works. + If you have a fun project you’d like to carry out, you can always discuss your idea with us to get tips and support.`)} + +
+

Medaljeriet

+ + + +
+ {lang(`Vi i Medaljeriet håller koll på vilka medaljer som ska köpas in och delas ut + och ger på så sätt corpsaftnarna och julkoncertsbanketten det där lilla extra! + Vi designar också de temaenliga julkoncertsmedaljerna varje år! + Utskottets finurliga tolkning av temat blir en fin souvenir till alla deltagande corps.`, + + `We in the Medal committee keep track of which medals are to be ordered and given out, + and thus we bring that extra shine to the dinner parties and the Christmas concert banquet! + We also design the Christmas concert medals in accordance with the concert's theme each year! + The committee's clever interpretation of the theme ends up as a nice souvenir for all participating corps.`)} + +
+ +

Import

+ +
+ {lang(`Vi i importen ser till att det finns den finaste ölen och cidern till ett överkomligt pris. + Därför åker vi på roadtrips över Öresund och med färjan över Fehmarnbältet för att köpa de bästa danska produkterna i Tyskland.`, + + `We from the import committee make sure with the finest beer and cider for an affordable prize. + Therefore we go on roadtrips across the Öresund and with the ferry over Fehmarn belt to buy the best Danish products in Germany.`)} + +
+

Export

+ +
+ {lang(`Vi i Exporten ser till att corpset aldrig går hungriga! + Vi fyller på med snacks, dryck och såklart billys! + Oavsett om det är rep, spelning, så ser vi till att corpset håller humöret på topp.`, + + `Exporten makes sure the corps never goes hungry! + We keep the snacks and drinks flowing and of course, plenty of Billy’s! + Whether it’s a rehearsal or a gig, we make sure the corps stays happy, energized, and ready to play.`)} + +
+
+
+ ); +}; + +export default Positions; + diff --git a/src/app/positions/page.tsx b/src/app/positions/page.tsx deleted file mode 100644 index d3ed8a48..00000000 --- a/src/app/positions/page.tsx +++ /dev/null @@ -1,130 +0,0 @@ -import { lang } from 'utils/language'; -import PositionInfobox from 'components/corps/position-infobox'; -import { api } from 'trpc/server'; -import { IconMail } from '@tabler/icons-react'; -import ActionIcon from 'components/input/action-icon'; - - -const styrelseOrder: Dictionary = { - "Ordförande": 1, - "ViceOrdförande": 2, - "Sekreterare": 3, - "Kassör": 4, -}; - - -const Positions = async () => { - const roles = await api.permission.getRoles.query(); - const ordoredBoardCorps = await Promise.all( - roles - .filter(role => Object.keys(styrelseOrder).includes(role.name)) - .sort((a, b) => styrelseOrder[a.name] - styrelseOrder[b.name]) - .flatMap(role => - role.corpsii.map(async (corps) => { - const result = await api.corps.get.query({ id: corps.id }); - if (!result) throw new Error("Corps not found"); - return result; - }) - ) - ); - - const TrivselCorps = await Promise.all( - roles - .filter(role => role.name === "Trivselombud") - .flatMap(role => - role.corpsii.map(async (corps) => { - const result = await api.corps.get.query({ id: corps.id }); - if (!result) throw new Error("Corps not found"); - return result; - }) - ) - ); - - return ( -
-

{lang('Ansvarsposter', 'Positions of responsibility')}

-
-
-

{lang('Styrelsen', 'The Board')}

- bla bla lite text -
- {ordoredBoardCorps.map((corps) => ( -
- {corps.roles.filter(role => Object.keys(styrelseOrder).includes(role.name))[0]?.name} - -
- )) - } -
-
-
- -
-
-

Trivselombud

- bla bla mer text. - {roles.filter((role) => ( - role.name == "Trivselombud" - )).map((role) => ( -
- {TrivselCorps.map((corps) => ( -
- -
- )) - } -
- ))} -
-
- -
-
-

Utskott

- bla bla mer text. - -
-

Pryl och Prov

- - - -
- blub - -
-

PR

- - - -
- blub - -
-

ITK

- - - -
- {lang(`ITK har ansvar för drift av alla Bleckhornens hemsidor, samt vidareutveckling av Blindtarmen. - Driftansvaret includerar blindtarmen, den publika hemsidan och vår interna wiki.`, - `ITK has responsibility for the operation of all Bleckhornens websites, as well as developing Blindtarmen. - The operational responsebility includes Blindtarmen, the public website, and our internal wiki`)} - - -
-
-
- ); -}; - -export default Positions; - diff --git a/src/components/navbar/index.tsx b/src/components/navbar/index.tsx index 88361c30..adb89379 100644 --- a/src/components/navbar/index.tsx +++ b/src/components/navbar/index.tsx @@ -3,6 +3,7 @@ import { IconClipboardList, IconHome, IconInfoSquare, + IconInfoHexagon, IconKey, IconLink, IconMicrophone2, @@ -61,6 +62,7 @@ const userTab: NavbarLinkGroup = { }, { label: lang('Sånger', 'Songs'), href: '/songs', icon: }, { label: lang('Länkar', 'Links'), href: '/links', icon: }, + { label: lang('Om Bleckhornen', 'About Bleckhornen'), href: '/info', icon: }, ], }; const adminTab: NavbarLinkGroup = { From 0c1a3f62169758a2b7eb0d2e13247c3eaac0b425 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linn=C3=A9a?= Date: Sun, 9 Nov 2025 19:41:44 +0100 Subject: [PATCH 7/7] Add samply link --- src/app/links/page.tsx | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/app/links/page.tsx b/src/app/links/page.tsx index f212ae0a..facbe356 100644 --- a/src/app/links/page.tsx +++ b/src/app/links/page.tsx @@ -49,6 +49,17 @@ const Links = () => { {lang('Notdriven', 'The Note Drive')} +
  • + + {lang( + 'Samply', + "Samply", + )} + +