Merged in chore/delete-unused-files (pull request #3346)

chore: Delete unused files

* Delete unused files

Ignore design-system for now


Approved-by: Joakim Jäderberg
This commit is contained in:
Anton Gunnarsson
2025-12-12 13:56:51 +00:00
parent c14b804c03
commit c153e0db50
42 changed files with 0 additions and 1679 deletions

View File

@@ -1,6 +0,0 @@
import { useBookingFlowContext } from "@scandic-hotels/booking-flow/hooks/useBookingFlowContext"
export function useIsUserLoggedIn() {
const { isLoggedIn } = useBookingFlowContext()
return isLoggedIn
}

View File

@@ -1,110 +0,0 @@
import { Suspense } from "react"
import ButtonLink from "@scandic-hotels/design-system/ButtonLink"
import { Typography } from "@scandic-hotels/design-system/Typography"
import { TrackingSDK } from "@scandic-hotels/tracking/TrackingSDK"
import Blocks from "@/components/Blocks"
import HeaderDynamicContent from "@/components/Headers/DynamicContent"
import Hero from "@/components/Hero"
import { HeroVideo } from "@/components/HeroVideo"
import MeetingPackageWidget from "@/components/MeetingPackageWidget"
import Sidebar from "@/components/Sidebar"
import SidebarSkeleton from "@/components/Sidebar/SidebarSkeleton"
import LinkChips from "@/components/TempDesignSystem/LinkChips"
import { staticPageVariants } from "./variants"
import styles from "./staticPage.module.css"
import type { StaticPageProps } from "./staticPage"
export default async function StaticPage({
content,
tracking,
pageType,
}: StaticPageProps) {
const { blocks, hero_image, hero_video, header, meeting_package } = content
return (
<>
<section className={staticPageVariants({ pageType })}>
<header className={styles.header}>
<div className={styles.headerContent}>
{header ? (
<>
<div className={styles.headerIntro}>
<Typography variant="Title/lg">
<h1 className={styles.heading}>{header.heading}</h1>
</Typography>
<Typography variant="Body/Lead text">
<p>{header.preamble}</p>
</Typography>
</div>
{header.top_primary_button?.url ? (
<ButtonLink
href={header.top_primary_button.url}
size="Medium"
variant="Tertiary"
className={styles.button}
>
{header.top_primary_button.title}
</ButtonLink>
) : null}
{header.navigation_links ? (
<LinkChips chips={header.navigation_links} />
) : null}
{"dynamic_content" in header &&
header.dynamic_content !== null &&
header.dynamic_content !== undefined ? (
<HeaderDynamicContent {...header.dynamic_content} />
) : null}
</>
) : null}
</div>
</header>
{hero_video || hero_image ? (
<div className={styles.heroWrapper}>
{hero_video ? (
<HeroVideo
className={styles.heroVideo}
src={hero_video.src}
focalPoint={hero_video.focalPoint}
captions={hero_video.captions}
/>
) : null}
{!hero_video && hero_image ? (
<Hero
className={styles.heroImage}
alt={hero_image.meta.alt || hero_image.meta.caption || ""}
src={hero_image.url}
focalPoint={hero_image.focalPoint}
dimensions={hero_image.dimensions}
/>
) : null}
</div>
) : null}
<div className={styles.contentContainer}>
<main className={styles.mainContent}>
{pageType === "collection" && meeting_package?.show_widget && (
<MeetingPackageWidget
destination={meeting_package.location}
className={styles.meetingPackageWidget}
/>
)}
{blocks ? <Blocks blocks={blocks} /> : null}
</main>
{"sidebar" in content && content.sidebar?.length ? (
<Suspense fallback={<SidebarSkeleton />}>
<Sidebar blocks={content.sidebar} />
</Suspense>
) : null}
</div>
</section>
<TrackingSDK pageData={tracking} />
</>
)
}

View File

@@ -1,112 +0,0 @@
.page {
padding-bottom: var(--Space-x9);
}
.header {
background-color: var(--Base-Surface-Subtle-Normal);
padding-bottom: var(--Space-x4);
}
.headerContent {
display: grid;
gap: var(--Space-x3);
max-width: var(--max-width-content);
margin: 0 auto;
}
.headerIntro {
display: grid;
max-width: var(--max-width-text-block);
gap: var(--Space-x3);
}
.heading {
color: var(--Text-Heading);
text-wrap: balance;
hyphens: auto;
}
.heroWrapper {
padding: var(--Space-x4) var(--Space-x2);
}
.heroImage,
.heroVideo {
max-width: var(--max-width-content);
margin: 0 auto;
display: flex;
}
.contentContainer {
width: 100%;
padding: var(--Space-x4) var(--Space-x2) 0;
}
.content .contentContainer {
display: grid;
grid-template-areas:
"main"
"sidebar";
gap: var(--Space-x4);
align-items: start;
}
.mainContent {
display: grid;
width: 100%;
gap: var(--Space-x6);
margin: 0 auto;
max-width: var(--max-width-content);
}
.content .mainContent {
grid-area: main;
}
.meetingPackageWidget {
border-radius: var(--Corner-radius-lg);
overflow: hidden;
}
.button {
width: fit-content;
}
@media (min-width: 768px) {
.contentContainer {
padding: var(--Space-x4) 0;
max-width: var(--max-width-content);
margin: 0 auto;
}
.heroContainer {
padding: var(--Space-x4) 0;
}
.headerIntro {
gap: var(--Space-x3);
}
}
/* Meeting booking widget changes design at 948px */
@media screen and (min-width: 948px) {
.meetingPackageWidget {
background-color: var(--Base-Surface-Primary-light-Normal);
box-shadow: 0px 4px 24px 0px rgba(0, 0, 0, 0.05);
}
}
@media (min-width: 1367px) {
.content .contentContainer {
grid-template-areas: "main sidebar";
grid-template-columns: var(--max-width-text-block) 1fr;
gap: var(--Space-x9);
}
.mainContent {
gap: var(--Space-x9);
padding: 0;
max-width: none;
margin: 0;
}
}

View File

@@ -1,14 +0,0 @@
import type { TrackingSDKPageData } from "@scandic-hotels/tracking/types"
import type { CollectionPage } from "@scandic-hotels/trpc/types/collectionPage"
import type { ContentPage } from "@scandic-hotels/trpc/types/contentPage"
import type { VariantProps } from "class-variance-authority"
import type { staticPageVariants } from "./variants"
export interface StaticPageProps
extends Omit<React.HTMLAttributes<HTMLDivElement>, "content">,
VariantProps<typeof staticPageVariants> {
pageType?: "collection" | "content"
content: CollectionPage["collection_page"] | ContentPage["content_page"]
tracking: TrackingSDKPageData
}

View File

@@ -1,15 +0,0 @@
import { cva } from "class-variance-authority"
import styles from "./staticPage.module.css"
export const staticPageVariants = cva(styles.page, {
variants: {
pageType: {
collection: styles.collection,
content: styles.content,
},
},
defaultVariants: {
pageType: "content",
},
})

View File

@@ -1,9 +0,0 @@
import type { FocalPoint } from "@scandic-hotels/common/utils/imageVault"
import type { Image } from "@scandic-hotels/trpc/types/image"
export interface HeroProps {
alt: string
src: string
focalPoint?: FocalPoint
dimensions?: Image["dimension"]
}

View File

@@ -1,102 +0,0 @@
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
import Link from "@scandic-hotels/design-system/OldDSLink"
import { Typography } from "@scandic-hotels/design-system/Typography"
import { getMembershipCards } from "@/lib/trpc/memoizedRequests"
import { getIntl } from "@/i18n"
import styles from "./membershipcards.module.css"
export default async function MembershipCardSlot() {
const intl = await getIntl()
const membershipCards = await getMembershipCards()
return (
<section className={styles.container}>
<div className={styles.content}>
<Typography variant="Title/Subtitle/md">
<h3>
{intl.formatMessage({
id: "myPages.myMembershipCards",
defaultMessage: "My membership cards",
})}
</h3>
</Typography>
</div>
{(membershipCards || []).map((card, idx) => (
<div className={styles.card} key={idx}>
<Typography variant="Title/Subtitle/md">
<h4 className={styles.subTitle}>
{intl.formatMessage(
{
id: "myPages.nameWithCardMembershipType",
defaultMessage: "Name: {cardMembershipType}",
},
{
cardMembershipType: card.membershipType,
}
)}
</h4>
</Typography>
<span>
{intl.formatMessage(
{
id: "myPages.currentPointsWithPoints",
defaultMessage: "Current Points: {points, number}",
},
{ points: card.currentPoints }
)}
</span>
<span>
{intl.formatMessage(
{
id: "myPages.memberSinceWithValue",
defaultMessage: "Member Since: {value}",
},
{
value: card.memberSince,
}
)}
</span>
<span data-hj-suppress>
{intl.formatMessage(
{
id: "myPages.numberWithValue",
defaultMessage: "Number: {membershipNumber}",
},
{
membershipNumber: card.membershipNumber,
}
)}
</span>
<span>
{intl.formatMessage(
{
id: "myPages.expirationDateWithDate",
defaultMessage: "Expiration Date: {expirationDate}",
},
{
expirationDate: card.expirationDate?.split("T")[0],
}
)}
</span>
</div>
))}
<Link href="#" variant="icon">
<MaterialIcon icon="add_circle" color="CurrentColor" />
<Typography
variant="Body/Paragraph/mdRegular"
className={styles.addNewCardText}
>
<p>
{intl.formatMessage({
id: "myPages.addNewCard",
defaultMessage: "Add new card",
})}
</p>
</Typography>
</Link>
</section>
)
}

View File

@@ -1,27 +0,0 @@
.container {
display: grid;
gap: var(--Space-x3);
max-width: 510px;
}
.content {
display: grid;
gap: var(--Space-x1);
}
.card {
margin-top: var(--Space-x4);
display: grid;
grid-template-columns: repeat(2, 1fr);
grid-template-rows: repeat(3, auto);
gap: var(--Space-x1);
}
.subTitle {
grid-column: span 2;
}
.addNewCardText {
text-decoration: underline;
color: var(--Scandic-Brand-Burgundy);
}

View File

@@ -1,9 +0,0 @@
import type { VariantProps } from "class-variance-authority"
import type { linkVariants } from "./variants"
export interface SectionLinkProps
extends React.PropsWithChildren<React.HTMLAttributes<HTMLSpanElement>>,
VariantProps<typeof linkVariants> {
link?: { href: string; text: string }
}

View File

@@ -1,6 +0,0 @@
import type { ApiImage } from "@scandic-hotels/trpc/types/hotel"
export type PreviewImagesProps = {
images: ApiImage[]
hotelName: string
}

View File

@@ -1,4 +0,0 @@
import type { User } from "@scandic-hotels/trpc/types/user"
export interface FriendProps
extends React.PropsWithChildren<Pick<User, "membership" | "name">> {}

View File

@@ -1,5 +0,0 @@
export type PointsColumnProps = {
title: string
subtitle?: string
value?: number | null
}

View File

@@ -1,34 +0,0 @@
"use client"
import { useIntl } from "react-intl"
import { useReactToPrint } from "react-to-print"
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
import { OldDSButton as Button } from "@scandic-hotels/design-system/OldDSButton"
import type { DownloadInvoiceProps } from "../../../../types/components/bookingConfirmation/actions/downloadInvoice"
export default function DownloadInvoice({ mainRef }: DownloadInvoiceProps) {
const intl = useIntl()
const reactToPrintFn = useReactToPrint({ contentRef: mainRef })
function downloadBooking() {
reactToPrintFn()
}
return (
<Button
intent="text"
onPress={downloadBooking}
size="small"
theme="base"
variant="icon"
wrapping
>
<MaterialIcon icon="download" color="CurrentColor" />
{intl.formatMessage({
id: "bookingConfirmation.downloadInvoice",
defaultMessage: "Download invoice",
})}
</Button>
)
}

View File

@@ -1,5 +0,0 @@
import type { MutableRefObject } from "react"
export interface DownloadInvoiceProps {
mainRef: MutableRefObject<HTMLElement | null>
}

View File

@@ -1,88 +0,0 @@
import type { CurrencyEnum } from "@scandic-hotels/common/constants/currency"
import type { RoomPackageCodeEnum } from "@scandic-hotels/trpc/enums/roomFilter"
import type { Room } from "@scandic-hotels/trpc/types/hotel"
import type { Package, PackageEnum } from "@scandic-hotels/trpc/types/packages"
import type {
Product,
RoomConfiguration,
RoomsAvailability,
} from "@scandic-hotels/trpc/types/roomAvailability"
import type { BookingCodeFilterEnum } from "../../stores/bookingCode-filter"
import type {
Rate,
Room as RoomBooking,
SelectRateBooking,
} from "../components/selectRate/selectRate"
export interface AvailabilityError {
details: string
error: string
}
interface Actions {
appendRegularRates: (
roomConfigurations: RoomConfiguration[] | undefined
) => void
closeSection: () => void
modifyRate: () => void
removeSelectedPackage: (code: PackageEnum) => void
removeSelectedPackages: () => void
selectFilter: (filter: BookingCodeFilterEnum) => void
selectPackages: (codes: PackageEnum[]) => void
selectRate: (rate: SelectedRate, isUserLoggedIn: boolean) => void
updateRooms: (rooms: RoomConfiguration[] | undefined) => void
}
export interface SelectedRate {
features: RoomConfiguration["features"]
product: Product
roomType: RoomConfiguration["roomType"]
roomTypeCode: RoomConfiguration["roomTypeCode"]
}
export interface SelectedRoom {
actions: Actions
bookingRoom: RoomBooking
isFetchingAdditionalRate: boolean
isFetchingPackages: boolean
rooms: RoomConfiguration[]
selectedFilter: BookingCodeFilterEnum | undefined
selectedPackages: Package[]
selectedRate: SelectedRate | null
}
interface DefaultFilterOptions {
code: RoomPackageCodeEnum
description: string
}
export interface RatesState {
activeRoom: number
booking: SelectRateBooking
hotelType: string | undefined
isRedemptionBooking: boolean
packageOptions: DefaultFilterOptions[]
rateSummary: Array<Rate | null>
rooms: SelectedRoom[]
roomCategories: Room[]
roomConfigurations: RoomConfiguration[][]
roomsPackages: Package[][]
roomsAvailability: (RoomsAvailability | AvailabilityError)[] | undefined
vat: number
defaultCurrency: CurrencyEnum
}
export interface InitialState
extends Pick<
RatesState,
"booking" | "hotelType" | "roomCategories" | "roomsAvailability" | "vat"
> {
initialActiveRoom?: number
pathname: string
labels: {
accessibilityRoom: string
allergyRoom: string
petRoom: string
}
}

View File

@@ -1,19 +0,0 @@
import { gql } from "graphql-tag"
import { Contact } from "../Contact.graphql"
export const ContactAside = gql`
fragment ContactAside on CurrentBlocksPageAsideContact {
contact {
contactConnection {
edges {
node {
...Contact
}
}
totalCount
}
}
}
${Contact}
`

View File

@@ -1,12 +0,0 @@
import { gql } from "graphql-tag"
import { Puff } from "../Puff.graphql"
export const PuffAside = gql`
fragment PuffAside on CurrentBlocksPageAsidePuff {
puff {
...Puff
}
}
${Puff}
`

View File

@@ -1,39 +0,0 @@
import { gql } from "graphql-tag"
export const ListItem = gql`
fragment ListItem on CurrentBlocksPageBlocksListBlockListItemsListItem {
list_item {
list_item_style
subtitle
title
}
}
`
export const ListItemExternalLink = gql`
fragment ListItemExternalLink on CurrentBlocksPageBlocksListBlockListItemsListItemExternalLink {
list_item_external_link {
link {
href
title
}
list_item_style
subtitle
}
}
`
export const ListBlock = gql`
fragment ListBlock on CurrentBlocksPageBlocksList {
list {
list_items {
__typename
...ListItem
...ListItemExternalLink
}
title
}
}
${ListItem}
${ListItemExternalLink}
`

View File

@@ -1,18 +0,0 @@
import { gql } from "graphql-tag"
import { Puff } from "../Puff.graphql"
export const PuffBlock = gql`
fragment PuffBlock on CurrentBlocksPageBlocksPuffs {
puffs {
puffs {
... on CurrentBlocksPageBlocksPuffsBlockPuffsPuff {
puff {
...Puff
}
}
}
}
}
${Puff}
`

View File

@@ -1,23 +0,0 @@
import { gql } from "graphql-tag"
import { SysAsset } from "../SysAsset.graphql"
export const TextBlock = gql`
fragment TextBlock on CurrentBlocksPageBlocksText {
text {
content {
embedded_itemsConnection {
totalCount
edges {
node {
__typename
...SysAsset
}
}
}
json
}
}
}
${SysAsset}
`

View File

@@ -1,13 +0,0 @@
import { gql } from "graphql-tag"
export const CurrentBlocksPageBreadcrumbs = gql`
fragment CurrentBlocksPageBreadcrumbs on CurrentBlocksPage {
breadcrumbs {
parents {
href
title
}
title
}
}
`

View File

@@ -1,32 +0,0 @@
import { gql } from "graphql-tag"
import { System } from "../System.graphql"
export const PromoCampaignPageBreadcrumb = gql`
fragment PromoCampaignPageBreadcrumb on PromoCampaignPage {
web {
breadcrumbs {
title
}
}
system {
...System
}
url
}
${System}
`
export const PromoCampaignPageBreadcrumbRef = gql`
fragment PromoCampaignPageBreadcrumbRef on PromoCampaignPage {
web {
breadcrumbs {
title
}
}
system {
...System
}
}
${System}
`

View File

@@ -1,71 +0,0 @@
import { gql } from "graphql-tag"
export const ContactExtraInfo = gql`
fragment ContactExtraInfo on ContactBlockSectionsExtraInfo {
extra_info {
text
}
}
`
export const ContactMailingAddress = gql`
fragment ContactMailingAddress on ContactBlockSectionsMailingAddress {
mailing_address {
city
country
name
street
zip
}
}
`
export const ContactPhone = gql`
fragment ContactPhone on ContactBlockSectionsPhone {
phone {
number
title
}
}
`
export const ContactTitle = gql`
fragment ContactTitle on ContactBlockSectionsTitle {
title {
text
}
}
`
export const ContactVisitingAddress = gql`
fragment ContactVisitingAddress on ContactBlockSectionsVisitingAddress {
visiting_address {
city
country
street
zip
}
}
`
export const Contact = gql`
fragment Contact on ContactBlock {
sections {
__typename
...ContactExtraInfo
...ContactMailingAddress
...ContactPhone
...ContactTitle
...ContactVisitingAddress
}
system {
locale
uid
}
}
${ContactExtraInfo}
${ContactMailingAddress}
${ContactPhone}
${ContactTitle}
${ContactVisitingAddress}
`

View File

@@ -1,32 +0,0 @@
import { gql } from "graphql-tag"
import { SysAsset } from "../SysAsset.graphql"
export const CurrentFooterAppDownloads = gql`
fragment CurrentFooterAppDownloads on CurrentFooter {
app_downloads {
title
app_store {
href
imageConnection {
edges {
node {
...SysAsset
}
}
}
}
google_play {
href
imageConnection {
edges {
node {
...SysAsset
}
}
}
}
}
}
${SysAsset}
`

View File

@@ -1,16 +0,0 @@
import { gql } from "graphql-tag"
import { SysAsset } from "../SysAsset.graphql"
export const Logo = gql`
fragment Logo on CurrentFooter {
logoConnection {
edges {
node {
...SysAsset
}
}
}
}
${SysAsset}
`

View File

@@ -1,33 +0,0 @@
import { gql } from "graphql-tag"
export const MainLinks = gql`
fragment MainLinks on Footer {
main_links {
title
open_in_new_tab
link {
href
title
}
pageConnection {
edges {
node {
__typename
... on AccountPage {
title
url
}
... on LoyaltyPage {
title
url
}
... on ContentPage {
title
url
}
}
}
}
}
}
`

View File

@@ -1,13 +0,0 @@
import { gql } from "graphql-tag"
export const Navigation = gql`
fragment Navigation on CurrentFooter {
navigation {
links {
href
title
}
title
}
}
`

View File

@@ -1,31 +0,0 @@
import { gql } from "graphql-tag"
import { AccountPageRef } from "../../AccountPage/Ref.graphql"
import { ContentPageRef } from "../../ContentPage/Ref.graphql"
import { LoyaltyPageRef } from "../../LoyaltyPage/Ref.graphql"
import { System } from "../../System.graphql"
export const MainLinksRef = gql`
fragment MainLinksRef on Footer {
__typename
main_links {
pageConnection {
edges {
node {
__typename
...LoyaltyPageRef
...ContentPageRef
...AccountPageRef
}
}
}
}
system {
...System
}
}
${System}
${LoyaltyPageRef}
${AccountPageRef}
${ContentPageRef}
`

View File

@@ -1,33 +0,0 @@
import { gql } from "graphql-tag"
import { AccountPageRef } from "../../AccountPage/Ref.graphql"
import { ContentPageRef } from "../../ContentPage/Ref.graphql"
import { LoyaltyPageRef } from "../../LoyaltyPage/Ref.graphql"
import { System } from "../../System.graphql"
export const SecondaryLinksRef = gql`
fragment SecondaryLinksRef on Footer {
__typename
secondary_links {
links {
pageConnection {
edges {
node {
__typename
...LoyaltyPageRef
...ContentPageRef
...AccountPageRef
}
}
}
}
}
system {
...System
}
}
${System}
${LoyaltyPageRef}
${AccountPageRef}
${ContentPageRef}
`

View File

@@ -1,24 +0,0 @@
import { gql } from "graphql-tag"
export const SecondaryLinks = gql`
fragment SecondaryLinks on Footer {
secondary_links {
title
links {
title
open_in_new_tab
pageConnection {
edges {
node {
__typename
}
}
}
link {
href
title
}
}
}
}
`

View File

@@ -1,21 +0,0 @@
import { gql } from "graphql-tag"
export const CurrentFooterSocialMedia = gql`
fragment CurrentFooterSocialMedia on CurrentFooter {
social_media {
title
facebook {
href
title
}
instagram {
href
title
}
twitter {
href
title
}
}
}
`

View File

@@ -1,19 +0,0 @@
import { gql } from "graphql-tag"
import { SysAsset } from "../SysAsset.graphql"
export const TripAdvisor = gql`
fragment TripAdvisor on CurrentFooter {
trip_advisor {
logoConnection {
edges {
node {
...SysAsset
}
}
}
title
}
}
${SysAsset}
`

View File

@@ -1,29 +0,0 @@
import { gql } from "graphql-tag"
import { AccountPageRef } from "../../AccountPage/Ref.graphql"
import { ContentPageRef } from "../../ContentPage/Ref.graphql"
import { LoyaltyPageRef } from "../../LoyaltyPage/Ref.graphql"
export const TertiaryLinksRef = gql`
fragment TertiaryLinksRef on Footer {
__typename
tertiary_links {
pageConnection {
edges {
node {
__typename
...LoyaltyPageRef
...ContentPageRef
...AccountPageRef
}
}
}
}
system {
...System
}
}
${LoyaltyPageRef}
${AccountPageRef}
${ContentPageRef}
`

View File

@@ -1,21 +0,0 @@
import { gql } from "graphql-tag"
export const Grid = gql`
fragment Grid on Grid {
columns {
span
rows {
rowConnection {
edges {
node {
__typename
... on Card {
title
}
}
}
}
}
}
}
`

View File

@@ -1,17 +0,0 @@
import { gql } from "graphql-tag"
import { SysAsset } from "./SysAsset.graphql"
export const Hero = gql`
fragment Hero on Hero {
imagesConnection {
totalCount
edges {
node {
...SysAsset
}
}
}
}
${SysAsset}
`

View File

@@ -1,14 +0,0 @@
import { gql } from "graphql-tag"
import { System } from "../System.graphql"
export const CurrentBlocksPageLink = gql`
fragment CurrentBlocksPageLink on CurrentBlocksPage {
title
url
system {
...System
}
}
${System}
`

View File

@@ -1,14 +0,0 @@
import { gql } from "graphql-tag"
import { System } from "../System.graphql"
export const CurrentBlocksPageLink = gql`
fragment CurrentBlocksPageLink on CurrentBlocksPage {
title
url
system {
...System
}
}
${System}
`

View File

@@ -1,22 +0,0 @@
import { gql } from "graphql-tag"
import { SysAsset } from "./SysAsset.graphql"
export const Preamble = gql`
fragment Preamble on CurrentBlocksPage {
preamble {
text {
json
embedded_itemsConnection(limit: 30) {
edges {
node {
__typename
...SysAsset
}
}
}
}
}
}
${SysAsset}
`

View File

@@ -1,25 +0,0 @@
import { gql } from "graphql-tag"
import { SysAsset } from "./SysAsset.graphql"
export const Puff = gql`
fragment Puff on Puff {
imageConnection {
edges {
node {
...SysAsset
}
}
}
puff_style
link {
href
title
}
text {
json
}
title
}
${SysAsset}
`

View File

@@ -1,19 +0,0 @@
import { gql } from "graphql-tag"
export const GetRewards = gql`
query GetRewards($locale: String!, $rewardIds: [String!]) {
all_reward(locale: $locale, where: { reward_id_in: $rewardIds }) {
items {
taxonomies {
term_uid
}
label
grouped_label
description
grouped_description
value
reward_id
}
}
}
`

View File

@@ -1,150 +0,0 @@
# GraphQL to TypeScript Converter
This script converts GraphQL files (`.graphql`) to TypeScript files (`.graphql.ts`) that use the `gql` template literal from `graphql-tag`.
## Features
- Converts individual fragments and queries into separate TypeScript exports
- Handles GraphQL imports (`#import`) and converts them to TypeScript imports
- Preserves fragment references and generates proper import statements
- Groups multiple imports from the same file
- Supports fragments, queries, mutations, and subscriptions
- Handles files with multiple operations
## Usage
### Basic Conversion
Convert all GraphQL files in the project:
```bash
yarn graphql:convert
```
Convert files matching a specific pattern:
```bash
yarn graphql:convert "packages/trpc/**/*.graphql"
```
Convert a single file:
```bash
yarn graphql:convert "path/to/file.graphql"
```
### Options
- `--dry-run`: Preview what files would be converted without actually converting them
- `--delete-originals`: Delete original `.graphql` files after successful conversion
- `--help`, `-h`: Show help message
### Examples
Preview conversion:
```bash
yarn graphql:convert --dry-run
```
Convert and delete originals:
```bash
yarn graphql:convert --delete-originals
```
Convert specific directory:
```bash
yarn graphql:convert "packages/trpc/lib/graphql/Fragments/**/*.graphql"
```
## Input Format
### GraphQL Fragment
```graphql
fragment Contact on ContactBlock {
sections {
__typename
}
}
```
### GraphQL Query with Imports
```graphql
#import "../Fragments/System.graphql"
#import "../Fragments/Metadata.graphql"
query GetData($locale: String!) {
data(locale: $locale) {
...System
...Metadata
}
}
```
## Output Format
### TypeScript Fragment
```typescript
import { gql } from "graphql-tag";
export const Contact = gql`
fragment Contact on ContactBlock {
sections {
__typename
}
}
`;
```
### TypeScript Query with Imports
```typescript
import { gql } from "graphql-tag";
import { System } from "../Fragments/System.graphql";
import { Metadata } from "../Fragments/Metadata.graphql";
export const GetData = gql`
query GetData($locale: String!) {
data(locale: $locale) {
...System
...Metadata
}
}
${System}
${Metadata}
`;
```
## How It Works
1. **Parse GraphQL Files**: Reads `.graphql` files and extracts imports and operations
2. **Handle Imports**: Converts `#import` statements to TypeScript imports by:
- Reading the imported file to determine export names
- Converting paths from `.graphql` to `.graphql.ts`
- Grouping multiple imports from the same file
3. **Extract Operations**: Identifies fragments, queries, mutations, and subscriptions
4. **Generate TypeScript**: Creates TypeScript files with:
- `gql` template literals
- Proper import statements
- Named exports for each operation
## Dependencies
- `glob`: For file pattern matching
- `tsx`: For TypeScript execution
- `@types/node`: For Node.js types
- `graphql-tag`: For the `gql` template literal (runtime dependency)
## Notes
- The script preserves the original file structure and naming
- Fragment references (`...FragmentName`) are preserved in the GraphQL content
- Multiple operations in a single file are split into separate exports
- Import conflicts are avoided by using the exact export names from referenced files
- Generated files maintain the same directory structure with `.graphql.ts` extension

View File

@@ -1,373 +0,0 @@
#!/usr/bin/env tsx
import * as fs from "fs";
import * as path from "path";
import { glob } from "glob";
interface ImportInfo {
fragmentName: string;
importPath: string;
variableName: string;
}
interface GraphQLFile {
content: string;
imports: ImportInfo[];
operations: Array<{ name: string; content: string }>;
}
/**
* Extracts individual fragments/operations from GraphQL content
*/
function extractOperations(
content: string
): Array<{ name: string; content: string }> {
const operations: Array<{ name: string; content: string }> = [];
// Split content into lines for processing
const lines = content.split("\n");
let currentOperation: { name: string; content: string[] } | null = null;
let braceCount = 0;
for (const line of lines) {
const trimmedLine = line.trim();
// Check if this line starts a new operation
const fragmentMatch = trimmedLine.match(/^fragment\s+(\w+)\s+on/);
const queryMatch = trimmedLine.match(/^query\s+(\w+)\s*[({]/);
const mutationMatch = trimmedLine.match(/^mutation\s+(\w+)\s*[({]/);
const subscriptionMatch = trimmedLine.match(
/^subscription\s+(\w+)\s*[({]/
);
if (fragmentMatch || queryMatch || mutationMatch || subscriptionMatch) {
// Finish previous operation if exists
if (currentOperation && currentOperation.content.length > 0) {
operations.push({
name: currentOperation.name,
content: currentOperation.content.join("\n").trim(),
});
}
// Start new operation
const operationName = (fragmentMatch ||
queryMatch ||
mutationMatch ||
subscriptionMatch)![1];
currentOperation = {
name: operationName,
content: [line],
};
// Count braces in the current line
braceCount =
(line.match(/{/g) || []).length -
(line.match(/}/g) || []).length;
} else if (currentOperation) {
// Add line to current operation
currentOperation.content.push(line);
// Update brace count
braceCount +=
(line.match(/{/g) || []).length -
(line.match(/}/g) || []).length;
// If we've closed all braces, this operation is complete
if (braceCount === 0 && trimmedLine.includes("}")) {
operations.push({
name: currentOperation.name,
content: currentOperation.content.join("\n").trim(),
});
currentOperation = null;
}
}
}
// Handle case where file ends without closing brace
if (currentOperation && currentOperation.content.length > 0) {
operations.push({
name: currentOperation.name,
content: currentOperation.content.join("\n").trim(),
});
}
return operations;
}
/**
* Generates TypeScript content from parsed GraphQL
*/
function generateTypeScriptContent(parsedFile: GraphQLFile): string {
const { imports, operations } = parsedFile;
let output = 'import { gql } from "graphql-tag"\n';
// Add imports for fragments - group by import path to avoid duplicates
if (imports.length > 0) {
output += "\n";
const importsByPath = new Map<string, string[]>();
for (const imp of imports) {
if (!importsByPath.has(imp.importPath)) {
importsByPath.set(imp.importPath, []);
}
if (
!importsByPath.get(imp.importPath)!.includes(imp.variableName)
) {
importsByPath.get(imp.importPath)!.push(imp.variableName);
}
}
for (const [importPath, variableNames] of importsByPath) {
output += `import { ${variableNames.join(", ")} } from "${importPath}"\n`;
}
}
output += "\n";
// Generate exports for each operation
if (operations.length === 0) {
// If no operation names found, use a default export
const defaultName = "GraphQLDocument";
const fragmentSubstitutions =
imports.length > 0
? "\n" +
imports.map((imp) => `\${${imp.variableName}}`).join("\n")
: "";
output += `export const ${defaultName} = gql\`\n${parsedFile.content}${fragmentSubstitutions}\n\`\n`;
} else {
for (let i = 0; i < operations.length; i++) {
const operation = operations[i];
const fragmentSubstitutions =
imports.length > 0
? "\n" +
imports.map((imp) => `\${${imp.variableName}}`).join("\n")
: "";
output += `export const ${operation.name} = gql\`\n${operation.content}${fragmentSubstitutions}\n\`\n`;
if (i < operations.length - 1) {
output += "\n";
}
}
}
return output;
}
/**
* Converts a GraphQL import path to a TypeScript import path
*/
function convertImportPath(graphqlPath: string): string {
// Remove the .graphql extension and add .graphql
const withoutExt = graphqlPath.replace(/\.graphql$/, "");
return `${withoutExt}.graphql`;
}
/**
* Gets the export names from a GraphQL file by analyzing the fragments it contains
*/
function getExportNamesFromGraphQLFile(filePath: string): string[] {
try {
const content = fs.readFileSync(filePath, "utf-8");
const operations = extractOperations(content);
return operations.map((op) => op.name);
} catch {
// If file doesn't exist or can't be read, try to infer from path
console.warn(
`Warning: Could not read ${filePath}, inferring export name from path`
);
return [getVariableNameFromPath(filePath)];
}
}
/**
* Extracts the expected variable name from a file path
*/
function getVariableNameFromPath(filePath: string): string {
const basename = path.basename(filePath, ".graphql");
// Convert kebab-case or snake_case to PascalCase
return basename
.split(/[-_]/)
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
.join("");
}
/**
* Parses a GraphQL file and extracts imports and content
*/
function parseGraphQLFile(filePath: string): GraphQLFile {
const content = fs.readFileSync(filePath, "utf-8");
const lines = content.split("\n");
const imports: ImportInfo[] = [];
const contentLines: string[] = [];
for (const line of lines) {
const trimmedLine = line.trim();
if (trimmedLine.startsWith("#import")) {
// Extract import path from #import "path"
const match = trimmedLine.match(/#import\s+"([^"]+)"/);
if (match) {
const importPath = match[1];
const fullImportPath = path.resolve(
path.dirname(filePath),
importPath
);
const exportNames =
getExportNamesFromGraphQLFile(fullImportPath);
const tsImportPath = convertImportPath(importPath);
// Add all exports from the imported file
for (const exportName of exportNames) {
imports.push({
fragmentName: exportName,
importPath: tsImportPath,
variableName: exportName,
});
}
}
} else if (trimmedLine && !trimmedLine.startsWith("#")) {
contentLines.push(line);
}
}
const cleanContent = contentLines.join("\n").trim();
const operations = extractOperations(cleanContent);
return {
content: cleanContent,
imports,
operations,
};
}
/**
* Converts a single GraphQL file to TypeScript
*/
function convertFile(graphqlPath: string): void {
try {
console.log(`Converting: ${graphqlPath}`);
const parsed = parseGraphQLFile(graphqlPath);
const tsContent = generateTypeScriptContent(parsed);
const tsPath = graphqlPath.replace(/\.graphql$/, ".graphql.ts");
fs.writeFileSync(tsPath, tsContent, "utf-8");
console.log(`✓ Created: ${tsPath}`);
// Optionally remove the original .graphql file
// Uncomment the next line if you want to delete the original files
// fs.unlinkSync(graphqlPath)
} catch (error) {
console.error(`Error converting ${graphqlPath}:`, error);
}
}
/**
* Main function to convert all GraphQL files
*/
async function main() {
const args = process.argv.slice(2);
if (args.includes("--help") || args.includes("-h")) {
console.log(`
GraphQL to TypeScript Converter
Converts GraphQL files (.graphql) to TypeScript files (.graphql.ts) using gql template literals.
Usage: tsx convert-graphql-to-ts.ts [options] [pattern]
Options:
--help, -h Show this help message
--dry-run Show what files would be converted without converting them
--delete-originals Delete original .graphql files after conversion
Examples:
tsx convert-graphql-to-ts.ts # Convert all .graphql files
tsx convert-graphql-to-ts.ts "packages/trpc/**/*.graphql" # Convert specific pattern
tsx convert-graphql-to-ts.ts --dry-run # Preview conversion
tsx convert-graphql-to-ts.ts --delete-originals # Convert and delete originals
Features:
• Converts fragments, queries, mutations, and subscriptions
• Handles GraphQL imports (#import) and converts to TypeScript imports
• Preserves fragment references and generates proper import statements
• Groups multiple imports from the same file
• Splits multiple operations into separate exports
`);
return;
}
const dryRun = args.includes("--dry-run");
const deleteOriginals = args.includes("--delete-originals");
// Get the pattern from args or use default
const pattern = args.find((arg) => !arg.startsWith("--")) || "**/*.graphql";
console.log(`🔍 Searching for GraphQL files with pattern: ${pattern}`);
try {
const files = await glob(pattern, {
ignore: ["**/*.graphql.ts", "**/node_modules/**"],
});
if (files.length === 0) {
console.log("❌ No GraphQL files found.");
return;
}
console.log(`📁 Found ${files.length} GraphQL files`);
if (dryRun) {
console.log("\n📋 Files that would be converted:");
files.forEach((file, index) => {
console.log(
` ${index + 1}. ${file}${file.replace(/\.graphql$/, ".graphql.ts")}`
);
});
console.log("\n🔍 --dry-run mode: No files were converted.");
return;
}
console.log("\n🔄 Converting files...");
let successCount = 0;
let errorCount = 0;
for (let i = 0; i < files.length; i++) {
const file = files[i];
try {
console.log(
`📝 [${i + 1}/${files.length}] Converting: ${file}`
);
convertFile(file);
successCount++;
if (deleteOriginals) {
fs.unlinkSync(file);
console.log(`🗑️ Deleted: ${file}`);
}
} catch (error) {
console.error(`❌ Error converting ${file}:`, error);
errorCount++;
}
}
console.log(`\n✅ Conversion complete!`);
console.log(` 📈 Successfully converted: ${successCount} files`);
if (errorCount > 0) {
console.log(` ❌ Errors: ${errorCount} files`);
}
if (deleteOriginals && successCount > 0) {
console.log(` 🗑️ Deleted: ${successCount} original files`);
}
} catch (error) {
console.error("❌ Error:", error);
process.exit(1);
}
}
// Run the script
if (import.meta.url === `file://${process.argv[1]}`) {
main().catch(console.error);
}