diff --git a/drizzle/0027_lonely_goliath.sql b/drizzle/0027_lonely_goliath.sql new file mode 100644 index 00000000..dff9c7d3 --- /dev/null +++ b/drizzle/0027_lonely_goliath.sql @@ -0,0 +1 @@ +ALTER TABLE `placeCategoryToPlaceCategoryGroup` ADD `highlight` boolean; \ No newline at end of file diff --git a/drizzle/meta/0027_snapshot.json b/drizzle/meta/0027_snapshot.json new file mode 100644 index 00000000..e6bc9c28 --- /dev/null +++ b/drizzle/meta/0027_snapshot.json @@ -0,0 +1,1824 @@ +{ + "version": "5", + "dialect": "mysql", + "id": "8bbc9370-c1e6-44fb-860e-06ce5a870846", + "prevId": "4c53b12c-2999-408a-92f1-df61177bcdb0", + "tables": { + "account": { + "name": "account", + "columns": { + "userId": { + "name": "userId", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider": { + "name": "provider", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "providerAccountId": { + "name": "providerAccountId", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "token_type": { + "name": "token_type", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "scope": { + "name": "scope", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "session_state": { + "name": "session_state", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "account_provider_providerAccountId_pk": { + "name": "account_provider_providerAccountId_pk", + "columns": [ + "provider", + "providerAccountId" + ] + } + }, + "uniqueConstraints": {} + }, + "session": { + "name": "session", + "columns": { + "sessionToken": { + "name": "sessionToken", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "userId": { + "name": "userId", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires": { + "name": "expires", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "session_sessionToken": { + "name": "session_sessionToken", + "columns": [ + "sessionToken" + ] + } + }, + "uniqueConstraints": {} + }, + "verificationToken": { + "name": "verificationToken", + "columns": { + "identifier": { + "name": "identifier", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "token": { + "name": "token", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires": { + "name": "expires", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "verificationToken_identifier_token_pk": { + "name": "verificationToken_identifier_token_pk", + "columns": [ + "identifier", + "token" + ] + } + }, + "uniqueConstraints": {} + }, + "externalLink": { + "name": "externalLink", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "placeId": { + "name": "placeId", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "routeId": { + "name": "routeId", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "isOfficialWebsite": { + "name": "isOfficialWebsite", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "title": { + "name": "title", + "type": "tinytext", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "externalLink_id": { + "name": "externalLink_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {} + }, + "externalLink_translation": { + "name": "externalLink_translation", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "externalLink_id": { + "name": "externalLink_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "locale": { + "name": "locale", + "type": "varchar(10)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "title": { + "name": "title", + "type": "tinytext", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "externalLink_translation_id": { + "name": "externalLink_translation_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {} + }, + "feature": { + "name": "feature", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "amountOfPeople": { + "name": "amountOfPeople", + "type": "enum('none','few','some','many','crowded')", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "difficulty": { + "name": "difficulty", + "type": "enum('accessible','normal','smallEffort','hard','dangerous')", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "groundType": { + "name": "groundType", + "type": "enum('sand','pebbles','rocks','concrete','dirt','pavimented')", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "hasBus": { + "name": "hasBus", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "hasParking": { + "name": "hasParking", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "parkingSpaces": { + "name": "parkingSpaces", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "hasToilet": { + "name": "hasToilet", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "hasRestaurant": { + "name": "hasRestaurant", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "hasDrinkingWater": { + "name": "hasDrinkingWater", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "hasShower": { + "name": "hasShower", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "hasLifeguard": { + "name": "hasLifeguard", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "hasLeisure": { + "name": "hasLeisure", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "isNudist": { + "name": "isNudist", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "hasUnofficialName": { + "name": "hasUnofficialName", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "hasInacurateLocation": { + "name": "hasInacurateLocation", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "date": { + "name": "date", + "type": "tinytext", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "isBoatOnly": { + "name": "isBoatOnly", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "trainingLevel": { + "name": "trainingLevel", + "type": "enum('noTraining','amateur','entryLevel','advanced','professional','elite')", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "hasMissingInfo": { + "name": "hasMissingInfo", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "height": { + "name": "height", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "depth": { + "name": "depth", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "depthMin": { + "name": "depthMin", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "depthMax": { + "name": "depthMax", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "scubaDivingLevel": { + "name": "scubaDivingLevel", + "type": "enum('discoverScubaDiving','openWater','advancedOpenWater','specialtyDiver','technicalDiver')", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "notThereAnymore": { + "name": "notThereAnymore", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "isOutOfTheMunicipality": { + "name": "isOutOfTheMunicipality", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "hasBench": { + "name": "hasBench", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "allowedAccess": { + "name": "allowedAccess", + "type": "enum('public','permissive','customers','permit','private','mixed')", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "dimensions": { + "name": "dimensions", + "type": "tinytext", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "price": { + "name": "price", + "type": "double", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "priceUnit": { + "name": "priceUnit", + "type": "enum('eur','eur/minute','eur/hour','eur/day')", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "isCovered": { + "name": "isCovered", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "duration": { + "name": "duration", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "distance": { + "name": "distance", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "slope": { + "name": "slope", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "timeToArrive": { + "name": "timeToArrive", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "placeToArriveFrom": { + "name": "placeToArriveFrom", + "type": "enum('townCenter','parking','beach','road')", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "isFreeWithLocalStamp": { + "name": "isFreeWithLocalStamp", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "howNarrow": { + "name": "howNarrow", + "type": "enum('extremlyNarrow','narrow','extraSpace','wide','veryWide')", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "difficultyNotes": { + "name": "difficultyNotes", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "priceNotes": { + "name": "priceNotes", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "allowedAccessNotes": { + "name": "allowedAccessNotes", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "hasInacurateLocationNotes": { + "name": "hasInacurateLocationNotes", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "hasMissingInfoNotes": { + "name": "hasMissingInfoNotes", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "notThereAnymoreNotes": { + "name": "notThereAnymoreNotes", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "feature_id": { + "name": "feature_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {} + }, + "feature_translation": { + "name": "feature_translation", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "feature_id": { + "name": "feature_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "locale": { + "name": "locale", + "type": "varchar(10)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "difficultyNotes": { + "name": "difficultyNotes", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "priceNotes": { + "name": "priceNotes", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "allowedAccessNotes": { + "name": "allowedAccessNotes", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "hasInacurateLocationNotes": { + "name": "hasInacurateLocationNotes", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "hasMissingInfoNotes": { + "name": "hasMissingInfoNotes", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "notThereAnymoreNotes": { + "name": "notThereAnymoreNotes", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "feature_translation_id": { + "name": "feature_translation_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {} + }, + "image": { + "name": "image", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "key": { + "name": "key", + "type": "varchar(1024)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "width": { + "name": "width", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "height": { + "name": "height", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "source": { + "name": "source", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "captureDate": { + "name": "captureDate", + "type": "date", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "alt": { + "name": "alt", + "type": "tinytext", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "blurDataURL": { + "name": "blurDataURL", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "image_id": { + "name": "image_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {} + }, + "placeCategory": { + "name": "placeCategory", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "icon": { + "name": "icon", + "type": "tinytext", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "color": { + "name": "color", + "type": "tinytext", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "hasVisitMission": { + "name": "hasVisitMission", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "order": { + "name": "order", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "tinytext", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "namePlural": { + "name": "namePlural", + "type": "tinytext", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "nameGender": { + "name": "nameGender", + "type": "enum('masculine','feminine')", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "placeCategory_id": { + "name": "placeCategory_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {} + }, + "placeCategory_translation": { + "name": "placeCategory_translation", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "placeCategory_id": { + "name": "placeCategory_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "locale": { + "name": "locale", + "type": "varchar(10)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "tinytext", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "namePlural": { + "name": "namePlural", + "type": "tinytext", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "nameGender": { + "name": "nameGender", + "type": "enum('masculine','feminine')", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "placeCategory_translation_id": { + "name": "placeCategory_translation_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {} + }, + "placeToPlaceCategory": { + "name": "placeToPlaceCategory", + "columns": { + "placeId": { + "name": "placeId", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "categoryId": { + "name": "categoryId", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "placeToPlaceCategory_categoryId_placeId_pk": { + "name": "placeToPlaceCategory_categoryId_placeId_pk", + "columns": [ + "categoryId", + "placeId" + ] + } + }, + "uniqueConstraints": {} + }, + "placeCategoryToPlaceCategoryGroup": { + "name": "placeCategoryToPlaceCategoryGroup", + "columns": { + "categoryGroupId": { + "name": "categoryGroupId", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "categoryId": { + "name": "categoryId", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "highlight": { + "name": "highlight", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "placeCategoryToPlaceCategoryGroup_categoryGroupId_categoryId_pk": { + "name": "placeCategoryToPlaceCategoryGroup_categoryGroupId_categoryId_pk", + "columns": [ + "categoryGroupId", + "categoryId" + ] + } + }, + "uniqueConstraints": {} + }, + "placeCategoryGroup": { + "name": "placeCategoryGroup", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "icon": { + "name": "icon", + "type": "tinytext", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "color": { + "name": "color", + "type": "tinytext", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "order": { + "name": "order", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "tinytext", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "nameGender": { + "name": "nameGender", + "type": "enum('masculine','feminine')", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "placeCategoryGroup_id": { + "name": "placeCategoryGroup_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {} + }, + "placeCategoryGroup_translation": { + "name": "placeCategoryGroup_translation", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "placeCategoryGroup_id": { + "name": "placeCategoryGroup_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "locale": { + "name": "locale", + "type": "varchar(10)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "tinytext", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "nameGender": { + "name": "nameGender", + "type": "enum('masculine','feminine')", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "placeCategoryGroup_translation_id": { + "name": "placeCategoryGroup_translation_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {} + }, + "placeListToPlace": { + "name": "placeListToPlace", + "columns": { + "placeListId": { + "name": "placeListId", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "placeId": { + "name": "placeId", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "addedAt": { + "name": "addedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "placeListToPlace_placeId_placeListId_pk": { + "name": "placeListToPlace_placeId_placeListId_pk", + "columns": [ + "placeId", + "placeListId" + ] + } + }, + "uniqueConstraints": {} + }, + "placeList": { + "name": "placeList", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "userId": { + "name": "userId", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "placeList_id": { + "name": "placeList_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {} + }, + "place": { + "name": "place", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "mainImageId": { + "name": "mainImageId", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "googleMapsId": { + "name": "googleMapsId", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "location": { + "name": "location", + "type": "POINT SRID 25831", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "mainCategoryId": { + "name": "mainCategoryId", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "featuresId": { + "name": "featuresId", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "verificationRequirementsId": { + "name": "verificationRequirementsId", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "importance": { + "name": "importance", + "type": "double", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "tinytext", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "place_id": { + "name": "place_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {} + }, + "place_translation": { + "name": "place_translation", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "place_id": { + "name": "place_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "locale": { + "name": "locale", + "type": "varchar(10)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "tinytext", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "place_translation_id": { + "name": "place_translation_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {} + }, + "routeCategory": { + "name": "routeCategory", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "icon": { + "name": "icon", + "type": "tinytext", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "color": { + "name": "color", + "type": "tinytext", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "hasVisitMission": { + "name": "hasVisitMission", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "order": { + "name": "order", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "tinytext", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "namePlural": { + "name": "namePlural", + "type": "tinytext", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "nameGender": { + "name": "nameGender", + "type": "enum('masculine','feminine')", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "routeCategory_id": { + "name": "routeCategory_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {} + }, + "routeCategory_translation": { + "name": "routeCategory_translation", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "routeCategory_id": { + "name": "routeCategory_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "locale": { + "name": "locale", + "type": "varchar(10)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "tinytext", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "namePlural": { + "name": "namePlural", + "type": "tinytext", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "nameGender": { + "name": "nameGender", + "type": "enum('masculine','feminine')", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "routeCategory_translation_id": { + "name": "routeCategory_translation_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {} + }, + "routeToRouteCategory": { + "name": "routeToRouteCategory", + "columns": { + "routeId": { + "name": "routeId", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "categoryId": { + "name": "categoryId", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "routeToRouteCategory_categoryId_routeId_pk": { + "name": "routeToRouteCategory_categoryId_routeId_pk", + "columns": [ + "categoryId", + "routeId" + ] + } + }, + "uniqueConstraints": {} + }, + "route": { + "name": "route", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "mainImageId": { + "name": "mainImageId", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "path": { + "name": "path", + "type": "MULTILINESTRING SRID 25831", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "mainCategoryId": { + "name": "mainCategoryId", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "featuresId": { + "name": "featuresId", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "verificationRequirementsId": { + "name": "verificationRequirementsId", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "importance": { + "name": "importance", + "type": "double", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "tinytext", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "route_id": { + "name": "route_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {} + }, + "routeToPlace": { + "name": "routeToPlace", + "columns": { + "routeId": { + "name": "routeId", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "placeId": { + "name": "placeId", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "routeToPlace_placeId_routeId_pk": { + "name": "routeToPlace_placeId_routeId_pk", + "columns": [ + "placeId", + "routeId" + ] + } + }, + "uniqueConstraints": {} + }, + "route_translation": { + "name": "route_translation", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "route_id": { + "name": "route_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "locale": { + "name": "locale", + "type": "varchar(10)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "tinytext", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "route_translation_id": { + "name": "route_translation_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {} + }, + "user": { + "name": "user", + "columns": { + "id": { + "name": "id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "hashedPassword": { + "name": "hashedPassword", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "emailVerified": { + "name": "emailVerified", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "image": { + "name": "image", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "role": { + "name": "role", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'user'" + }, + "visitedPlaceListId": { + "name": "visitedPlaceListId", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "user_id": { + "name": "user_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "user_email_unique": { + "name": "user_email_unique", + "columns": [ + "email" + ] + } + } + }, + "verificationRequirement": { + "name": "verificationRequirement", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "isLocationRequired": { + "name": "isLocationRequired", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "maxLocationDistance": { + "name": "maxLocationDistance", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "verificationRequirement_id": { + "name": "verificationRequirement_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {} + }, + "verification": { + "name": "verification", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "placeId": { + "name": "placeId", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "userId": { + "name": "userId", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "validatedOn": { + "name": "validatedOn", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP" + }, + "deviceLocation": { + "name": "deviceLocation", + "type": "POINT SRID 25831", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "deviceLocationAccuracy": { + "name": "deviceLocationAccuracy", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "verification_id": { + "name": "verification_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {} + } + }, + "schemas": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json index 791f83ce..e704c212 100644 --- a/drizzle/meta/_journal.json +++ b/drizzle/meta/_journal.json @@ -190,6 +190,13 @@ "when": 1709681591503, "tag": "0026_thick_ben_parker", "breakpoints": true + }, + { + "idx": 27, + "version": "5", + "when": 1710885080731, + "tag": "0027_lonely_goliath", + "breakpoints": true } ] } \ No newline at end of file diff --git a/src/app/[locale]/(app)/explore/_components/back-link.tsx b/src/app/[locale]/(app)/explore/_components/back-link.tsx index 9cc0e594..f6ea74ed 100644 --- a/src/app/[locale]/(app)/explore/_components/back-link.tsx +++ b/src/app/[locale]/(app)/explore/_components/back-link.tsx @@ -4,13 +4,11 @@ import { IconArrowLeft } from '@tabler/icons-react' import { useTranslations } from 'next-intl' import { FC } from 'react' import { Link, usePathname } from '~/navigation' -import { getExploreTab } from './explore-tabs' export const BackLink: FC = () => { const t = useTranslations('explore') const pathname = usePathname() - const tab = getExploreTab(pathname) - if (tab) return null + if (pathname !== '/explore/') return null return ( diff --git a/src/app/[locale]/(app)/explore/_components/categories-grid.tsx b/src/app/[locale]/(app)/explore/_components/categories-grid.tsx deleted file mode 100644 index db54475d..00000000 --- a/src/app/[locale]/(app)/explore/_components/categories-grid.tsx +++ /dev/null @@ -1,97 +0,0 @@ -'use client' - -import { Button } from '@nextui-org/button' -import { Card } from '@nextui-org/card' -import { Skeleton } from '@nextui-org/react' -import { useTranslations } from 'next-intl' -import { FC, useState } from 'react' -import { CategoryIcon } from '~/components/icons/category-icon' -import { cn } from '~/helpers/cn' -import { Link } from '~/navigation' -import { PlaceCategory } from '~/server/db/constants/placeCategories' - -const ITEMS = 6 -export const CategoriesGrid: FC<{ - categories: PlaceCategory[] - type: 'place' | 'route' -}> = ({ categories, type }) => { - const t = useTranslations('explore') - const [showingAll, setShowingAll] = useState(false) - - return ( -
-

- {t('categories')} -

- - - {categories.length > ITEMS && ( - <> - - - - )} -
- ) -} - -export const CategoriesGridSkeleton: FC = () => { - return ( -
- -
- {[...Array(3 * 2)].map((_, i) => ( - - ))} -
- -
- ) -} diff --git a/src/app/[locale]/(app)/explore/_components/explore-tabs.tsx b/src/app/[locale]/(app)/explore/_components/explore-tabs.tsx deleted file mode 100644 index 0b079228..00000000 --- a/src/app/[locale]/(app)/explore/_components/explore-tabs.tsx +++ /dev/null @@ -1,79 +0,0 @@ -'use client' - -import { Tab, Tabs } from '@nextui-org/tabs' -import { - Icon, - IconBuildingStore, - IconMapPin, - IconRoute, -} from '@tabler/icons-react' -import { useTranslations } from 'next-intl' -import { FC } from 'react' -import { cn } from '~/helpers/cn' -import { usePathname } from '~/navigation' - -export const exploreTabsData = [ - { - key: 'places', - icon: IconMapPin, - }, - { - key: 'bussinesses', - icon: IconBuildingStore, - }, - { - key: 'routes', - icon: IconRoute, - }, -] as const satisfies { - key: string - icon: Icon -}[] - -export const getExploreTab = (pathname: string) => { - const tab = pathname.match(/^\/explore\/([^/]+)$/)?.[1] - if (!tab || !exploreTabsData.some((t) => t.key === tab)) return null - return tab as (typeof exploreTabsData)[number]['key'] -} - -export const ExploreTabs: FC<{ - className?: string -}> = ({ className }) => { - const t = useTranslations('explore') - const pathname = usePathname() - const tab = getExploreTab(pathname) - - if (!tab) return null - - return ( - - {(item) => { - const Icon = item.icon - return ( - - - {t(`tabs.${item.key}`)} - - } - href={`/explore/${item.key}`} - /> - ) - }} - - ) -} diff --git a/src/app/[locale]/(app)/explore/_components/links-to-categories-modal.tsx b/src/app/[locale]/(app)/explore/_components/links-to-categories-modal.tsx new file mode 100644 index 00000000..b647c62a --- /dev/null +++ b/src/app/[locale]/(app)/explore/_components/links-to-categories-modal.tsx @@ -0,0 +1,95 @@ +'use client' + +import { + Button, + Modal, + ModalBody, + ModalContent, + ModalFooter, + ModalHeader, + ScrollShadow, + useDisclosure, +} from '@nextui-org/react' +import { useTranslations } from 'next-intl' +import { FC } from 'react' +import { PlaceMarker } from '~/components/generic/place-marker' +import { Link } from '~/navigation' +import { CategoryGroupListItem } from './list-category-groups' + +export const LinksToCategoriesModal: FC<{ + group: CategoryGroupListItem +}> = ({ group }) => { + const t = useTranslations('explore') + const { isOpen, onOpen, onOpenChange } = useDisclosure() + + const sortedCategories = group.categories.sort((a, b) => + a.category.namePlural.localeCompare(b.category.namePlural) + ) + + return ( + <> + + + + + {(onClose) => ( + <> + + {group.name} + + + +
    + {sortedCategories.map(({ category }) => { + const categoryLink = + group.type === 'place' + ? (`/explore/search?placeCategory=${category.id}` as const) + : (`/explore/search?routeCategory=${category.id}` as const) + + return ( + + ) + })} +
+
+
+ + + + + )} +
+
+ + ) +} diff --git a/src/app/[locale]/(app)/explore/_components/list-category-groups.tsx b/src/app/[locale]/(app)/explore/_components/list-category-groups.tsx new file mode 100644 index 00000000..7997bfa2 --- /dev/null +++ b/src/app/[locale]/(app)/explore/_components/list-category-groups.tsx @@ -0,0 +1,95 @@ +import { Card } from '@nextui-org/card' +import { CardBody, Skeleton } from '@nextui-org/react' +import { FC } from 'react' +import { CategoryIcon } from '~/components/icons/category-icon' +import { cn } from '~/helpers/cn' +import { colorClasses } from '~/helpers/color-classes' +import { Link } from '~/navigation' +import { ApiRouterOutput } from '~/server/api/router' +import { LinksToCategoriesModal } from './links-to-categories-modal' + +type BaseCategoryGroup = NonNullable< + ApiRouterOutput['explore']['listCategoryGroups'] +>[number] + +export type CategoryGroupListItem = Omit< + BaseCategoryGroup, + 'placeCategories' +> & { + categories: BaseCategoryGroup['placeCategories'] + type: 'place' | 'route' +} + +export const ListCategoryGroups: FC<{ + group: CategoryGroupListItem +}> = ({ group }) => { + const highlightedCategories = group.categories + .filter((category) => category.highlight) + .map(({ category }) => category) + + return ( + <> +

+ {group.name} +

+ + + + + ) +} + +export const ListCategoryGroupsSkeleton: FC = () => { + return ( + <> + +
+ {Array.from({ length: 6 }).map((_, i) => ( + + ))} +
+ + + ) +} diff --git a/src/app/[locale]/(app)/explore/_components/map-drawer.tsx b/src/app/[locale]/(app)/explore/_components/map-drawer.tsx index 7735fb65..c4df0825 100644 --- a/src/app/[locale]/(app)/explore/_components/map-drawer.tsx +++ b/src/app/[locale]/(app)/explore/_components/map-drawer.tsx @@ -1,6 +1,5 @@ import type { FC, PropsWithChildren } from 'react' import { cn } from '~/helpers/cn' -import { ExploreTabs } from './explore-tabs' type MapDrawerProps = PropsWithChildren<{ classNames?: { @@ -24,8 +23,6 @@ export const MapDrawer: FC = ({
- -
{children}
diff --git a/src/app/[locale]/(app)/explore/bussinesses/loading.tsx b/src/app/[locale]/(app)/explore/bussinesses/loading.tsx deleted file mode 100644 index 6bbc5ff6..00000000 --- a/src/app/[locale]/(app)/explore/bussinesses/loading.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { FC } from 'react' -import { CategoriesGridSkeleton } from '../_components/categories-grid' -import { ListPlacesOfCategorySkeleton } from '../_components/list-items-of-category' - -const Loading: FC = () => { - return ( - <> - - - {[...Array(5)].map((_, i) => ( - - ))} - - ) -} - -export default Loading diff --git a/src/app/[locale]/(app)/explore/bussinesses/page.tsx b/src/app/[locale]/(app)/explore/bussinesses/page.tsx deleted file mode 100644 index 8035cd61..00000000 --- a/src/app/[locale]/(app)/explore/bussinesses/page.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import type { Metadata } from 'next' -import { getTranslations, unstable_setRequestLocale } from 'next-intl/server' -import type { FC } from 'react' -import { - onlyTranslatableLocales, - parseLocale, - type LocaleRouteParams, -} from '~/i18n' -import { getTrpc } from '~/server/get-server-thing' -import { CategoriesGrid } from '../_components/categories-grid' -import { ListItemsOfCategory } from '../_components/list-items-of-category' -import { OverrideMainMap } from '../_components/override-main-map' - -export async function generateMetadata({ - params, -}: LocaleRouteParams): Promise { - const t = await getTranslations({ - locale: params.locale, - namespace: 'explore.bussinesses', - }) - return { - title: t('meta.title'), - description: t('meta.description'), - } -} - -const BussinessesPage: FC = async ({ params }) => { - const locale = parseLocale(params.locale) - unstable_setRequestLocale(locale) - - const trpc = await getTrpc() - const placesByCategory = await trpc.explore.listBussinesses({ - locale: onlyTranslatableLocales(locale), - }) - - return ( - <> - - - - -
- {placesByCategory.map(({ places, ...category }) => ( - - ))} -
- - ) -} - -export default BussinessesPage diff --git a/src/app/[locale]/(app)/explore/layout.tsx b/src/app/[locale]/(app)/explore/layout.tsx index 5461a59d..a84d93ff 100644 --- a/src/app/[locale]/(app)/explore/layout.tsx +++ b/src/app/[locale]/(app)/explore/layout.tsx @@ -23,7 +23,7 @@ const ExploreLayout: FC = async ({ params, children }) => { classNames={{ controls: 'bottom-6', expanded: [ - 'min-h-[calc(100dvh_-_65px_-_57px_-_57px)]', + 'min-h-[calc(100dvh_-_65px_-_140px_-_57px)]', 'sticky top-16 grow', ], collapsed: 'h-48', diff --git a/src/app/[locale]/(app)/explore/loading.tsx b/src/app/[locale]/(app)/explore/loading.tsx new file mode 100644 index 00000000..5780610f --- /dev/null +++ b/src/app/[locale]/(app)/explore/loading.tsx @@ -0,0 +1,14 @@ +import { FC } from 'react' +import { ListCategoryGroupsSkeleton } from './_components/list-category-groups' + +const Loading: FC = () => { + return ( +
+ {[...Array(5)].map((_, i) => ( + + ))} +
+ ) +} + +export default Loading diff --git a/src/app/[locale]/(app)/explore/page.tsx b/src/app/[locale]/(app)/explore/page.tsx index 1c5b2654..d10a3d2a 100644 --- a/src/app/[locale]/(app)/explore/page.tsx +++ b/src/app/[locale]/(app)/explore/page.tsx @@ -1,8 +1,17 @@ import type { Metadata } from 'next' import { getTranslations, unstable_setRequestLocale } from 'next-intl/server' import type { FC } from 'react' -import { parseLocale, type LocaleRouteParams } from '~/i18n' -import { redirect } from '~/navigation' +import { + onlyTranslatableLocales, + parseLocale, + type LocaleRouteParams, +} from '~/i18n' +import { getTrpc } from '~/server/get-server-thing' +import { + CategoryGroupListItem, + ListCategoryGroups, +} from './_components/list-category-groups' +import { OverrideMainMap } from './_components/override-main-map' export async function generateMetadata({ params, @@ -21,9 +30,42 @@ const ExplorePage: FC = async ({ params }) => { const locale = parseLocale(params.locale) unstable_setRequestLocale(locale) - redirect('/explore/places') + const t = await getTranslations('explore') - return null + const trpc = await getTrpc() + const categoryGroups = await trpc.explore.listCategoryGroups({ + locale: onlyTranslatableLocales(locale), + }) + const routeCategories = await trpc.explore.listRouteCategories({ + locale: onlyTranslatableLocales(locale), + }) + + const groups: CategoryGroupListItem[] = categoryGroups.map((group) => ({ + ...group, + categories: group.placeCategories, + type: 'place', + })) + + groups.splice(1, 0, { + name: t('category-groups.routes'), + id: 9999999, + order: null, + categories: routeCategories.map((category) => ({ + category, + highlight: [1, 2, 3, 5].includes(category.id), + })), + type: 'route', + }) + + return ( +
+ + + {groups.map((group) => ( + + ))} +
+ ) } export default ExplorePage diff --git a/src/app/[locale]/(app)/explore/_components/place-details-verificate-button.tsx b/src/app/[locale]/(app)/explore/places/[placeId]/_components/place-details-verificate-button.tsx similarity index 87% rename from src/app/[locale]/(app)/explore/_components/place-details-verificate-button.tsx rename to src/app/[locale]/(app)/explore/places/[placeId]/_components/place-details-verificate-button.tsx index c8bf1a69..40e0570a 100644 --- a/src/app/[locale]/(app)/explore/_components/place-details-verificate-button.tsx +++ b/src/app/[locale]/(app)/explore/places/[placeId]/_components/place-details-verificate-button.tsx @@ -5,7 +5,7 @@ import { shotConfettiStars } from '~/helpers/confetti' import { VerificateButton, VerificateButtonProps, -} from '../../missions/_components/verificate-button' +} from '../../../../missions/_components/verificate-button' export const PlaceDetailsVerificateButton: FC = ( props diff --git a/src/app/[locale]/(app)/explore/_components/place-details.tsx b/src/app/[locale]/(app)/explore/places/[placeId]/_components/place-details.tsx similarity index 94% rename from src/app/[locale]/(app)/explore/_components/place-details.tsx rename to src/app/[locale]/(app)/explore/places/[placeId]/_components/place-details.tsx index aa6d872d..d37f4c47 100644 --- a/src/app/[locale]/(app)/explore/_components/place-details.tsx +++ b/src/app/[locale]/(app)/explore/places/[placeId]/_components/place-details.tsx @@ -6,8 +6,8 @@ import { IconTitle } from '~/components/generic/icon-title' import { MarkdownContent } from '~/components/generic/markdown-content' import { OptimizedImage } from '~/components/generic/optimized-image' import { ApiRouterOutput } from '~/server/api/router' -import { VisitMissionsAcordion } from '../../missions/_components/visit-missions-acordion' -import { FeaturesBlock } from './features-block' +import { FeaturesBlock } from '../../../../../../../components/features/features-block' +import { VisitMissionsAcordion } from '../../../../missions/_components/visit-missions-acordion' import { PlaceDetailsVerificateButton } from './place-details-verificate-button' import { ViewMoreImagesButtonAndDialog } from './view-more-images-button-and-dialog' diff --git a/src/app/[locale]/(app)/explore/_components/view-more-images-button-and-dialog.tsx b/src/app/[locale]/(app)/explore/places/[placeId]/_components/view-more-images-button-and-dialog.tsx similarity index 100% rename from src/app/[locale]/(app)/explore/_components/view-more-images-button-and-dialog.tsx rename to src/app/[locale]/(app)/explore/places/[placeId]/_components/view-more-images-button-and-dialog.tsx diff --git a/src/app/[locale]/(app)/explore/places/[placeId]/page.tsx b/src/app/[locale]/(app)/explore/places/[placeId]/page.tsx index 9f18022f..c0ecaac4 100644 --- a/src/app/[locale]/(app)/explore/places/[placeId]/page.tsx +++ b/src/app/[locale]/(app)/explore/places/[placeId]/page.tsx @@ -12,7 +12,7 @@ import { import { db } from '~/server/db/db' import { getTrpc } from '~/server/get-server-thing' import { OverrideMainMap } from '../../_components/override-main-map' -import { PlaceDetails } from '../../_components/place-details' +import { PlaceDetails } from './_components/place-details' type Params = LocaleRouteParams<{ placeId: string }> diff --git a/src/app/[locale]/(app)/explore/places/loading.tsx b/src/app/[locale]/(app)/explore/places/loading.tsx deleted file mode 100644 index 6bbc5ff6..00000000 --- a/src/app/[locale]/(app)/explore/places/loading.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { FC } from 'react' -import { CategoriesGridSkeleton } from '../_components/categories-grid' -import { ListPlacesOfCategorySkeleton } from '../_components/list-items-of-category' - -const Loading: FC = () => { - return ( - <> - - - {[...Array(5)].map((_, i) => ( - - ))} - - ) -} - -export default Loading diff --git a/src/app/[locale]/(app)/explore/places/page.tsx b/src/app/[locale]/(app)/explore/places/page.tsx deleted file mode 100644 index 57a323c8..00000000 --- a/src/app/[locale]/(app)/explore/places/page.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import type { Metadata } from 'next' -import { getTranslations, unstable_setRequestLocale } from 'next-intl/server' -import type { FC } from 'react' -import { - onlyTranslatableLocales, - parseLocale, - type LocaleRouteParams, -} from '~/i18n' -import { getTrpc } from '~/server/get-server-thing' -import { CategoriesGrid } from '../_components/categories-grid' -import { ListItemsOfCategory } from '../_components/list-items-of-category' -import { OverrideMainMap } from '../_components/override-main-map' - -export async function generateMetadata({ - params, -}: LocaleRouteParams): Promise { - const t = await getTranslations({ - locale: params.locale, - namespace: 'explore.places', - }) - return { - title: t('meta.title'), - description: t('meta.description'), - } -} - -const PlacesPage: FC = async ({ params }) => { - const locale = parseLocale(params.locale) - unstable_setRequestLocale(locale) - - const trpc = await getTrpc() - const placesByCategory = await trpc.explore.listPlaces({ - locale: onlyTranslatableLocales(locale), - }) - - return ( - <> - - - - -
- {placesByCategory.map((category) => ( - - ))} -
- - ) -} - -export default PlacesPage diff --git a/src/app/[locale]/(app)/explore/_components/route-details.tsx b/src/app/[locale]/(app)/explore/routes/[routeId]/_components/route-details.tsx similarity index 92% rename from src/app/[locale]/(app)/explore/_components/route-details.tsx rename to src/app/[locale]/(app)/explore/routes/[routeId]/_components/route-details.tsx index 1b93afa6..0afba2a6 100644 --- a/src/app/[locale]/(app)/explore/_components/route-details.tsx +++ b/src/app/[locale]/(app)/explore/routes/[routeId]/_components/route-details.tsx @@ -6,8 +6,8 @@ import { IconTitle } from '~/components/generic/icon-title' import { MarkdownContent } from '~/components/generic/markdown-content' import { OptimizedImage } from '~/components/generic/optimized-image' import { ApiRouterOutput } from '~/server/api/router' -import { FeaturesBlock } from './features-block' -import { ViewMoreImagesButtonAndDialog } from './view-more-images-button-and-dialog' +import { FeaturesBlock } from '../../../../../../../components/features/features-block' +import { ViewMoreImagesButtonAndDialog } from '../../../places/[placeId]/_components/view-more-images-button-and-dialog' type Route = NonNullable diff --git a/src/app/[locale]/(app)/explore/routes/[routeId]/page.tsx b/src/app/[locale]/(app)/explore/routes/[routeId]/page.tsx index 9fbbe323..e2b7e8d6 100644 --- a/src/app/[locale]/(app)/explore/routes/[routeId]/page.tsx +++ b/src/app/[locale]/(app)/explore/routes/[routeId]/page.tsx @@ -12,7 +12,7 @@ import { import { db } from '~/server/db/db' import { getTrpc } from '~/server/get-server-thing' import { OverrideMainMap } from '../../_components/override-main-map' -import { RouteDetails } from '../../_components/route-details' +import { RouteDetails } from './_components/route-details' type Params = LocaleRouteParams<{ routeId: string }> diff --git a/src/app/[locale]/(app)/explore/routes/loading.tsx b/src/app/[locale]/(app)/explore/routes/loading.tsx deleted file mode 100644 index 6bbc5ff6..00000000 --- a/src/app/[locale]/(app)/explore/routes/loading.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { FC } from 'react' -import { CategoriesGridSkeleton } from '../_components/categories-grid' -import { ListPlacesOfCategorySkeleton } from '../_components/list-items-of-category' - -const Loading: FC = () => { - return ( - <> - - - {[...Array(5)].map((_, i) => ( - - ))} - - ) -} - -export default Loading diff --git a/src/app/[locale]/(app)/explore/routes/page.tsx b/src/app/[locale]/(app)/explore/routes/page.tsx deleted file mode 100644 index 85eac9bb..00000000 --- a/src/app/[locale]/(app)/explore/routes/page.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import type { Metadata } from 'next' -import { getTranslations, unstable_setRequestLocale } from 'next-intl/server' -import type { FC } from 'react' -import { - onlyTranslatableLocales, - parseLocale, - type LocaleRouteParams, -} from '~/i18n' -import { getTrpc } from '~/server/get-server-thing' -import { CategoriesGrid } from '../_components/categories-grid' -import { ListItemsOfCategory } from '../_components/list-items-of-category' -import { OverrideMainMap } from '../_components/override-main-map' - -export async function generateMetadata({ - params, -}: LocaleRouteParams): Promise { - const t = await getTranslations({ - locale: params.locale, - namespace: 'explore.routes', - }) - return { - title: t('meta.title'), - description: t('meta.description'), - } -} - -const RoutesPage: FC = async ({ params }) => { - const locale = parseLocale(params.locale) - unstable_setRequestLocale(locale) - - const trpc = await getTrpc() - const routesByCategory = await trpc.explore.listRoutes({ - locale: onlyTranslatableLocales(locale), - }) - - return ( - <> - - - - -
- {routesByCategory.map((category) => ( - - ))} -
- - ) -} - -export default RoutesPage diff --git a/src/app/[locale]/(app)/explore/_components/place-list.tsx b/src/app/[locale]/(app)/explore/search/_components/place-list.tsx similarity index 100% rename from src/app/[locale]/(app)/explore/_components/place-list.tsx rename to src/app/[locale]/(app)/explore/search/_components/place-list.tsx diff --git a/src/app/[locale]/(app)/explore/search/page.tsx b/src/app/[locale]/(app)/explore/search/page.tsx index 03ce4de9..4af8ff33 100644 --- a/src/app/[locale]/(app)/explore/search/page.tsx +++ b/src/app/[locale]/(app)/explore/search/page.tsx @@ -4,12 +4,12 @@ import type { FC } from 'react' import { LocaleRouteParams, onlyTranslatableLocales, parseLocale } from '~/i18n' import { getTrpc } from '~/server/get-server-thing' import { OverrideMainMap } from '../_components/override-main-map' -import { PlaceList } from '../_components/place-list' +import { PlaceList } from './_components/place-list' type PageParams = LocaleRouteParams & { searchParams: { - placeCategory: string - routeCategory: string // TODO: use this to search + placeCategory?: string + routeCategory?: string // TODO: use this to search } } export async function generateMetadata({ @@ -35,7 +35,9 @@ const ExplorePage: FC = async ({ params, searchParams }) => { const trpc = await getTrpc() const places = await trpc.places.search({ locale: onlyTranslatableLocales(locale), - placeCategory: Number(searchParams.placeCategory), + placeCategory: searchParams.placeCategory + ? Number(searchParams.placeCategory) + : null, }) const placeIds = new Set(places.map((place) => place.id)) diff --git a/src/app/[locale]/(app)/profile/_components/tabs/visited-tab.tsx b/src/app/[locale]/(app)/profile/_components/tabs/visited-tab.tsx index cd4fac5e..6fcbb6aa 100644 --- a/src/app/[locale]/(app)/profile/_components/tabs/visited-tab.tsx +++ b/src/app/[locale]/(app)/profile/_components/tabs/visited-tab.tsx @@ -2,7 +2,7 @@ import { useLocale, useTranslations } from 'next-intl' import { FC } from 'react' import { onlyTranslatableLocales } from '~/i18n' import { trpc } from '~/trpc' -import { PlaceList } from '../../../explore/_components/place-list' +import { PlaceList } from '../../../explore/search/_components/place-list' export const VisitedTab: FC = () => { const t = useTranslations('profile.tabs.visited') diff --git a/src/components/admin-only/external-links-editor.tsx b/src/components/admin-only/external-links-editor.tsx index 8cb2f5a8..cd764a86 100644 --- a/src/components/admin-only/external-links-editor.tsx +++ b/src/components/admin-only/external-links-editor.tsx @@ -11,7 +11,7 @@ import { useFormContext, useWatch, } from 'react-hook-form' -import { getLinkData } from '~/app/[locale]/(app)/explore/_components/named-websites' +import { getLinkData } from '~/components/features/named-websites' import { cn } from '~/helpers/cn' import { makeGoogleMapsUrl } from '~/helpers/data/google-maps-id' import { Link } from '~/navigation' diff --git a/src/app/[locale]/(app)/explore/_components/features-block.tsx b/src/components/features/features-block.tsx similarity index 100% rename from src/app/[locale]/(app)/explore/_components/features-block.tsx rename to src/components/features/features-block.tsx diff --git a/src/app/[locale]/(app)/explore/_components/named-websites.ts b/src/components/features/named-websites.ts similarity index 100% rename from src/app/[locale]/(app)/explore/_components/named-websites.ts rename to src/components/features/named-websites.ts diff --git a/src/helpers/color-classes.ts b/src/helpers/color-classes.ts index 8798b349..782683ef 100644 --- a/src/helpers/color-classes.ts +++ b/src/helpers/color-classes.ts @@ -163,6 +163,47 @@ export const colorClasses = { pink: 'text-pink-600', rose: 'text-rose-600', }, + + shadow: { + gray: 'shadow-gray-700/60', + red: 'shadow-red-500/60', + orange: 'shadow-orange-500/60', + amber: 'shadow-amber-500/60', + yellow: 'shadow-yellow-500/60', + lime: 'shadow-lime-500/60', + green: 'shadow-green-500/60', + emerald: 'shadow-emerald-500/60', + teal: 'shadow-teal-500/60', + cyan: 'shadow-cyan-500/60', + sky: 'shadow-sky-500/60', + blue: 'shadow-blue-500/60', + indigo: 'shadow-indigo-500/60', + violet: 'shadow-violet-500/60', + purple: 'shadow-purple-500/60', + fuchsia: 'shadow-fuchsia-500/60', + pink: 'shadow-pink-500/60', + rose: 'shadow-rose-500/60', + }, + gradientBg: { + gray: 'from-gray-900 to-gray-700', + red: 'from-red-600 to-red-500', + orange: 'from-orange-600 to-orange-500', + amber: 'from-amber-600 to-amber-500', + yellow: 'from-yellow-600 to-yellow-500', + lime: 'from-lime-600 to-lime-500', + green: 'from-green-600 to-green-500', + emerald: 'from-emerald-600 to-emerald-500', + teal: 'from-teal-600 to-teal-500', + cyan: 'from-cyan-600 to-cyan-500', + sky: 'from-sky-600 to-sky-500', + blue: 'from-blue-600 to-blue-500', + indigo: 'from-indigo-600 to-indigo-500', + violet: 'from-violet-600 to-violet-500', + purple: 'from-purple-600 to-purple-500', + fuchsia: 'from-fuchsia-600 to-fuchsia-500', + pink: 'from-pink-600 to-pink-500', + rose: 'from-rose-600 to-rose-500', + }, } as const satisfies { [x: string]: Record } diff --git a/src/messages/ca.json b/src/messages/ca.json index a4f8b5f7..116ca11a 100644 --- a/src/messages/ca.json +++ b/src/messages/ca.json @@ -366,6 +366,7 @@ }, "see-all": "Veure {gender, select, masculine {tots} feminine {totes} other {tots}}", "show-all": "Mostrar {gender, select, masculine {tots} feminine {totes} other {tots}}", + "see-all-categories": "Veure totes les categories", "show-less": "Mostrar menys", "see-more": "Veure més", "categories": "Categories", @@ -373,9 +374,8 @@ "related-missions": "Missions relacionades", "detailed-info": "Informació detallada", "maps-app": "App de mapes", - "tabs": { - "places": "Llocs", - "bussinesses": "Negocis", + "close": "Tancar", + "category-groups": { "routes": "Rutes" } }, diff --git a/src/messages/en.json b/src/messages/en.json index 1efa2a98..77c9c807 100644 --- a/src/messages/en.json +++ b/src/messages/en.json @@ -366,6 +366,7 @@ }, "see-all": "See all", "show-all": "Show all", + "see-all-categories": "See all categories", "show-less": "Show less", "see-more": "See more", "categories": "Categories", @@ -373,9 +374,8 @@ "related-missions": "Related missions", "detailed-info": "Detailed info", "maps-app": "Maps app", - "tabs": { - "places": "Places", - "bussinesses": "Bussinesses", + "close": "Close", + "category-groups": { "routes": "Routes" } }, diff --git a/src/server/api/router/explore.ts b/src/server/api/router/explore.ts index 12f8c3cb..663c1bdb 100644 --- a/src/server/api/router/explore.ts +++ b/src/server/api/router/explore.ts @@ -1,187 +1,90 @@ import { z } from 'zod' import { translatableLocales } from '~/i18n' import { db } from '~/server/db/db' -import { placeCategoriesToPlaceCategoryGroups } from '~/server/db/schema' import { ascNullsEnd } from '~/server/helpers/order-by' -import { doSomethingAfterExecute } from '~/server/helpers/translations/on-execute' import { flattenTranslationsOnExecute, withTranslations, } from '~/server/helpers/translations/query/with-translations' import { publicProcedure, router } from '~/server/trpc' -const placeCategoryGroupIdByName = { - nature: 1, - activities: 2, - historyAndCulture: 3, - infrastructure: 4, - shops: 5, - restaurants: 6, - leisureAndSports: 7, - services: 8, - foodAndSupermarkets: 9, - accommodation: 10, -} as const satisfies Record - -const makeGetCategoriesWithPlaces = (categoryGroupIds: number[]) => - doSomethingAfterExecute( - flattenTranslationsOnExecute( - db.query.placeCategories - .findMany( - withTranslations({ - where: (category, { inArray }) => - inArray( - category.id, - db - .select({ - data: placeCategoriesToPlaceCategoryGroups.categoryId, - }) - .from(placeCategoriesToPlaceCategoryGroups) - .where( - inArray( - placeCategoriesToPlaceCategoryGroups.categoryGroupId, - categoryGroupIds - ) - ) - ), +const getCategoryGroups = flattenTranslationsOnExecute( + db.query.placeCategoryGroups + .findMany( + withTranslations({ + columns: { + id: true, + name: true, + order: true, + }, + with: { + placeCategories: { + columns: { + highlight: true, + }, with: { - mainPlaces: withTranslations({ + category: withTranslations({ columns: { id: true, - name: true, - importance: true, - }, - with: { - mainImage: true, + namePlural: true, + icon: true, + color: true, + order: true, }, }), - secondaryPlaces: { - with: { - place: withTranslations({ - columns: { - id: true, - name: true, - importance: true, - }, - with: { - mainImage: true, - }, - }), - }, - }, - }, - orderBy: (category) => [ascNullsEnd(category.order)], - }) - ) - .prepare() - ), - (categories) => - categories.map(({ mainPlaces, secondaryPlaces, ...category }) => ({ - ...category, - places: [...mainPlaces, ...secondaryPlaces.map(({ place }) => place)] - .filter(isFirstOccurence) - .sort(sortByImportance), - })) - ) - -const getCategoriesWithPlacesForBussinesses = makeGetCategoriesWithPlaces([ - placeCategoryGroupIdByName.shops, - placeCategoryGroupIdByName.restaurants, - placeCategoryGroupIdByName.leisureAndSports, - placeCategoryGroupIdByName.services, - placeCategoryGroupIdByName.foodAndSupermarkets, - placeCategoryGroupIdByName.accommodation, -]) -const getCategoriesWithPlacesForPlaces = makeGetCategoriesWithPlaces([ - placeCategoryGroupIdByName.nature, - placeCategoryGroupIdByName.activities, - placeCategoryGroupIdByName.historyAndCulture, - placeCategoryGroupIdByName.infrastructure, -]) - -const getCategoriesWithRoutes = doSomethingAfterExecute( - flattenTranslationsOnExecute( - db.query.routeCategories - .findMany( - withTranslations({ - with: { - mainRoutes: withTranslations({ - columns: { - id: true, - name: true, - importance: true, - }, - with: { - mainImage: true, - }, - }), - secondaryRoutes: { - with: { - route: withTranslations({ - columns: { - id: true, - name: true, - importance: true, - }, - with: { - mainImage: true, - }, - }), - }, }, }, - orderBy: (category) => [ascNullsEnd(category.order)], - }) - ) - .prepare() - ), - (categories) => - categories.map(({ mainRoutes, secondaryRoutes, ...category }) => ({ - ...category, - routes: [...mainRoutes, ...secondaryRoutes.map(({ route }) => route)] - .filter(isFirstOccurence) - .sort(sortByImportance), - })) + }, + orderBy: (group) => [ascNullsEnd(group.order)], + }) + ) + .prepare() ) -export const exploreRouter = router({ - listBussinesses: publicProcedure - .input(z.object({ locale: z.enum(translatableLocales).nullable() })) - .query(async ({ input }) => { - return await getCategoriesWithPlacesForBussinesses.execute({ - locale: input.locale, +const getRouteCategories = flattenTranslationsOnExecute( + db.query.routeCategories + .findMany( + withTranslations({ + columns: { + id: true, + namePlural: true, + icon: true, + color: true, + order: true, + }, + orderBy: (group) => [ascNullsEnd(group.order)], }) - }), - listPlaces: publicProcedure + ) + .prepare() +) + +export const exploreRouter = router({ + listCategoryGroups: publicProcedure .input(z.object({ locale: z.enum(translatableLocales).nullable() })) .query(async ({ input }) => { - return await getCategoriesWithPlacesForPlaces.execute({ + const groups = await getCategoryGroups.execute({ locale: input.locale, }) + + return groups.map((group) => ({ + ...group, + placeCategories: group.placeCategories.sort( + ({ category: a }, { category: b }) => sortByOrder(a, b) + ), + })) }), - listRoutes: publicProcedure + listRouteCategories: publicProcedure .input(z.object({ locale: z.enum(translatableLocales).nullable() })) .query(async ({ input }) => { - return await getCategoriesWithRoutes.execute({ + return await getRouteCategories.execute({ locale: input.locale, }) }), }) -const sortByImportance = ( - a: T, - b: T -) => { - if (a.importance === b.importance) return 0 - if (a.importance === null) return 1 - if (b.importance === null) return -1 - return a.importance > b.importance ? -1 : 1 -} - -const isFirstOccurence = ( - place: T, - index: number, - self: T[] -) => { - return self.findIndex((p) => p.id === place.id) === index +const sortByOrder = (a: T, b: T) => { + if (a.order === b.order) return 0 + if (a.order === null) return 1 + if (b.order === null) return -1 + return a.order < b.order ? -1 : 1 } diff --git a/src/server/db/schema/placeCategoryGroups.ts b/src/server/db/schema/placeCategoryGroups.ts index 62ce12c2..bc8920eb 100644 --- a/src/server/db/schema/placeCategoryGroups.ts +++ b/src/server/db/schema/placeCategoryGroups.ts @@ -1,5 +1,11 @@ import { relations } from 'drizzle-orm' -import { int, mysqlTable, primaryKey, tinytext } from 'drizzle-orm/mysql-core' +import { + boolean, + int, + mysqlTable, + primaryKey, + tinytext, +} from 'drizzle-orm/mysql-core' import { mysqlTableWithTranslations } from '../../helpers/translations/db-tables' import { dbColor, dbGender, dbIcon } from '../utilities' import { placeCategories } from './placeCategories' @@ -36,6 +42,7 @@ export const placeCategoriesToPlaceCategoryGroups = mysqlTable( { categoryGroupId: int('categoryGroupId').notNull(), categoryId: int('categoryId').notNull(), + highlight: boolean('highlight'), }, (table) => { return {