feat: Introduce new coffee shop images and remove tRPC integration.
24
bun.lock
@@ -11,10 +11,6 @@
|
|||||||
"@radix-ui/react-separator": "^1.1.8",
|
"@radix-ui/react-separator": "^1.1.8",
|
||||||
"@radix-ui/react-slot": "^1.2.4",
|
"@radix-ui/react-slot": "^1.2.4",
|
||||||
"@t3-oss/env-nextjs": "^0.12.0",
|
"@t3-oss/env-nextjs": "^0.12.0",
|
||||||
"@tanstack/react-query": "^5.69.0",
|
|
||||||
"@trpc/client": "^11.0.0",
|
|
||||||
"@trpc/react-query": "^11.0.0",
|
|
||||||
"@trpc/server": "^11.0.0",
|
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"leaflet": "^1.9.4",
|
"leaflet": "^1.9.4",
|
||||||
@@ -24,8 +20,6 @@
|
|||||||
"react": "19.2.1",
|
"react": "19.2.1",
|
||||||
"react-dom": "19.2.1",
|
"react-dom": "19.2.1",
|
||||||
"react-leaflet": "^5.0.0",
|
"react-leaflet": "^5.0.0",
|
||||||
"server-only": "^0.0.1",
|
|
||||||
"superjson": "^2.2.1",
|
|
||||||
"tailwind-merge": "^3.4.0",
|
"tailwind-merge": "^3.4.0",
|
||||||
"zod": "^3.24.2",
|
"zod": "^3.24.2",
|
||||||
},
|
},
|
||||||
@@ -317,16 +311,6 @@
|
|||||||
|
|
||||||
"@tailwindcss/postcss": ["@tailwindcss/postcss@4.1.17", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "@tailwindcss/node": "4.1.17", "@tailwindcss/oxide": "4.1.17", "postcss": "^8.4.41", "tailwindcss": "4.1.17" } }, "sha512-+nKl9N9mN5uJ+M7dBOOCzINw94MPstNR/GtIhz1fpZysxL/4a+No64jCBD6CPN+bIHWFx3KWuu8XJRrj/572Dw=="],
|
"@tailwindcss/postcss": ["@tailwindcss/postcss@4.1.17", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "@tailwindcss/node": "4.1.17", "@tailwindcss/oxide": "4.1.17", "postcss": "^8.4.41", "tailwindcss": "4.1.17" } }, "sha512-+nKl9N9mN5uJ+M7dBOOCzINw94MPstNR/GtIhz1fpZysxL/4a+No64jCBD6CPN+bIHWFx3KWuu8XJRrj/572Dw=="],
|
||||||
|
|
||||||
"@tanstack/query-core": ["@tanstack/query-core@5.90.12", "", {}, "sha512-T1/8t5DhV/SisWjDnaiU2drl6ySvsHj1bHBCWNXd+/T+Hh1cf6JodyEYMd5sgwm+b/mETT4EV3H+zCVczCU5hg=="],
|
|
||||||
|
|
||||||
"@tanstack/react-query": ["@tanstack/react-query@5.90.12", "", { "dependencies": { "@tanstack/query-core": "5.90.12" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-graRZspg7EoEaw0a8faiUASCyJrqjKPdqJ9EwuDRUF9mEYJ1YPczI9H+/agJ0mOJkPCJDk0lsz5QTrLZ/jQ2rg=="],
|
|
||||||
|
|
||||||
"@trpc/client": ["@trpc/client@11.7.2", "", { "peerDependencies": { "@trpc/server": "11.7.2", "typescript": ">=5.7.2" } }, "sha512-OQxqUMfpDvjcszo9dbnqWQXnW2L5IbrKSz2H7l8s+mVM3EvYw7ztQ/gjFIN3iy0NcamiQfd4eE6qjcb9Lm+63A=="],
|
|
||||||
|
|
||||||
"@trpc/react-query": ["@trpc/react-query@11.7.2", "", { "peerDependencies": { "@tanstack/react-query": "^5.80.3", "@trpc/client": "11.7.2", "@trpc/server": "11.7.2", "react": ">=18.2.0", "react-dom": ">=18.2.0", "typescript": ">=5.7.2" } }, "sha512-IcLDMqx2mvlGRxkr0/m37TtPvRQ8nonITH3EwYv436x0Igx8eduR9z4tdgGBsjJY9e5W1G7cZ4zKCwrizSimFQ=="],
|
|
||||||
|
|
||||||
"@trpc/server": ["@trpc/server@11.7.2", "", { "peerDependencies": { "typescript": ">=5.7.2" } }, "sha512-AgB26PXY69sckherIhCacKLY49rxE2XP5h38vr/KMZTbLCL1p8IuIoKPjALTcugC2kbyQ7Lbqo2JDVfRSmPmfQ=="],
|
|
||||||
|
|
||||||
"@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="],
|
"@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="],
|
||||||
|
|
||||||
"@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
|
"@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
|
||||||
@@ -479,8 +463,6 @@
|
|||||||
|
|
||||||
"convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="],
|
"convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="],
|
||||||
|
|
||||||
"copy-anything": ["copy-anything@4.0.5", "", { "dependencies": { "is-what": "^5.2.0" } }, "sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA=="],
|
|
||||||
|
|
||||||
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
|
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
|
||||||
|
|
||||||
"csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="],
|
"csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="],
|
||||||
@@ -701,8 +683,6 @@
|
|||||||
|
|
||||||
"is-weakset": ["is-weakset@2.0.4", "", { "dependencies": { "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" } }, "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ=="],
|
"is-weakset": ["is-weakset@2.0.4", "", { "dependencies": { "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" } }, "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ=="],
|
||||||
|
|
||||||
"is-what": ["is-what@5.5.0", "", {}, "sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw=="],
|
|
||||||
|
|
||||||
"isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="],
|
"isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="],
|
||||||
|
|
||||||
"isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
|
"isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
|
||||||
@@ -887,8 +867,6 @@
|
|||||||
|
|
||||||
"semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
|
"semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
|
||||||
|
|
||||||
"server-only": ["server-only@0.0.1", "", {}, "sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA=="],
|
|
||||||
|
|
||||||
"set-function-length": ["set-function-length@1.2.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2" } }, "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg=="],
|
"set-function-length": ["set-function-length@1.2.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2" } }, "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg=="],
|
||||||
|
|
||||||
"set-function-name": ["set-function-name@2.0.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "functions-have-names": "^1.2.3", "has-property-descriptors": "^1.0.2" } }, "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ=="],
|
"set-function-name": ["set-function-name@2.0.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "functions-have-names": "^1.2.3", "has-property-descriptors": "^1.0.2" } }, "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ=="],
|
||||||
@@ -933,8 +911,6 @@
|
|||||||
|
|
||||||
"styled-jsx": ["styled-jsx@5.1.6", "", { "dependencies": { "client-only": "0.0.1" }, "peerDependencies": { "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" } }, "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA=="],
|
"styled-jsx": ["styled-jsx@5.1.6", "", { "dependencies": { "client-only": "0.0.1" }, "peerDependencies": { "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" } }, "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA=="],
|
||||||
|
|
||||||
"superjson": ["superjson@2.2.6", "", { "dependencies": { "copy-anything": "^4" } }, "sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA=="],
|
|
||||||
|
|
||||||
"supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
|
"supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
|
||||||
|
|
||||||
"supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="],
|
"supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="],
|
||||||
|
|||||||
@@ -22,10 +22,6 @@
|
|||||||
"@radix-ui/react-separator": "^1.1.8",
|
"@radix-ui/react-separator": "^1.1.8",
|
||||||
"@radix-ui/react-slot": "^1.2.4",
|
"@radix-ui/react-slot": "^1.2.4",
|
||||||
"@t3-oss/env-nextjs": "^0.12.0",
|
"@t3-oss/env-nextjs": "^0.12.0",
|
||||||
"@tanstack/react-query": "^5.69.0",
|
|
||||||
"@trpc/client": "^11.0.0",
|
|
||||||
"@trpc/react-query": "^11.0.0",
|
|
||||||
"@trpc/server": "^11.0.0",
|
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"leaflet": "^1.9.4",
|
"leaflet": "^1.9.4",
|
||||||
@@ -35,8 +31,6 @@
|
|||||||
"react": "19.2.1",
|
"react": "19.2.1",
|
||||||
"react-dom": "19.2.1",
|
"react-dom": "19.2.1",
|
||||||
"react-leaflet": "^5.0.0",
|
"react-leaflet": "^5.0.0",
|
||||||
"server-only": "^0.0.1",
|
|
||||||
"superjson": "^2.2.1",
|
|
||||||
"tailwind-merge": "^3.4.0",
|
"tailwind-merge": "^3.4.0",
|
||||||
"zod": "^3.24.2"
|
"zod": "^3.24.2"
|
||||||
},
|
},
|
||||||
|
|||||||
BIN
public/images/shops/7th-st-cafe.jpg
Normal file
|
After Width: | Height: | Size: 148 KiB |
BIN
public/images/shops/alee-s-cafe.jpg
Normal file
|
After Width: | Height: | Size: 66 KiB |
BIN
public/images/shops/all-star-bagels.jpg
Normal file
|
After Width: | Height: | Size: 43 KiB |
BIN
public/images/shops/amami-kitchen.jpg
Normal file
|
After Width: | Height: | Size: 95 KiB |
BIN
public/images/shops/barnes-noble-caf.jpg
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
public/images/shops/cornerstone-kitchen.jpg
Normal file
|
After Width: | Height: | Size: 49 KiB |
BIN
public/images/shops/culture-coffee.jpg
Normal file
|
After Width: | Height: | Size: 104 KiB |
BIN
public/images/shops/cycleup-coffee.jpg
Normal file
|
After Width: | Height: | Size: 69 KiB |
BIN
public/images/shops/dunkin.jpg
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
public/images/shops/gram-s-eatery.jpg
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
public/images/shops/panera-bread.jpg
Normal file
|
After Width: | Height: | Size: 43 KiB |
BIN
public/images/shops/paris-bakery-caf.jpg
Normal file
|
After Width: | Height: | Size: 108 KiB |
BIN
public/images/shops/starbucks-coffee.jpg
Normal file
|
After Width: | Height: | Size: 58 KiB |
BIN
public/images/shops/starbucks-giant.jpg
Normal file
|
After Width: | Height: | Size: 51 KiB |
BIN
public/images/shops/tastecraft-cafe.jpg
Normal file
|
After Width: | Height: | Size: 33 KiB |
@@ -1,34 +0,0 @@
|
|||||||
import { fetchRequestHandler } from "@trpc/server/adapters/fetch";
|
|
||||||
import { type NextRequest } from "next/server";
|
|
||||||
|
|
||||||
import { env } from "~/env";
|
|
||||||
import { appRouter } from "~/server/api/root";
|
|
||||||
import { createTRPCContext } from "~/server/api/trpc";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This wraps the `createTRPCContext` helper and provides the required context for the tRPC API when
|
|
||||||
* handling a HTTP request (e.g. when you make requests from Client Components).
|
|
||||||
*/
|
|
||||||
const createContext = async (req: NextRequest) => {
|
|
||||||
return createTRPCContext({
|
|
||||||
headers: req.headers,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const handler = (req: NextRequest) =>
|
|
||||||
fetchRequestHandler({
|
|
||||||
endpoint: "/api/trpc",
|
|
||||||
req,
|
|
||||||
router: appRouter,
|
|
||||||
createContext: () => createContext(req),
|
|
||||||
onError:
|
|
||||||
env.NODE_ENV === "development"
|
|
||||||
? ({ path, error }) => {
|
|
||||||
console.error(
|
|
||||||
`❌ tRPC failed on ${path ?? "<no-path>"}: ${error.message}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
: undefined,
|
|
||||||
});
|
|
||||||
|
|
||||||
export { handler as GET, handler as POST };
|
|
||||||
@@ -3,7 +3,6 @@ import "~/styles/globals.css";
|
|||||||
import { type Metadata } from "next";
|
import { type Metadata } from "next";
|
||||||
import { PT_Serif } from "next/font/google";
|
import { PT_Serif } from "next/font/google";
|
||||||
|
|
||||||
import { TRPCReactProvider } from "~/trpc/react";
|
|
||||||
import { ThemeProvider } from "~/components/ThemeProvider";
|
import { ThemeProvider } from "~/components/ThemeProvider";
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
@@ -30,7 +29,7 @@ export default function RootLayout({
|
|||||||
enableSystem
|
enableSystem
|
||||||
disableTransitionOnChange
|
disableTransitionOnChange
|
||||||
>
|
>
|
||||||
<TRPCReactProvider>{children}</TRPCReactProvider>
|
{children}
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -43,7 +43,13 @@ export default function Drawer({ shop, onClose }: DrawerProps) {
|
|||||||
{/* Header Image */}
|
{/* Header Image */}
|
||||||
<div className="h-56 relative flex-shrink-0 bg-muted/20">
|
<div className="h-56 relative flex-shrink-0 bg-muted/20">
|
||||||
{imageLoading && (
|
{imageLoading && (
|
||||||
<div className="absolute inset-0 z-10 flex items-center justify-center">
|
<div
|
||||||
|
className="absolute inset-0 z-10 flex items-center justify-center"
|
||||||
|
style={{
|
||||||
|
maskImage: 'linear-gradient(to bottom, black 50%, transparent 100%)',
|
||||||
|
WebkitMaskImage: 'linear-gradient(to bottom, black 50%, transparent 100%)'
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Skeleton className="h-full w-full absolute inset-0" />
|
<Skeleton className="h-full w-full absolute inset-0" />
|
||||||
<Coffee className="h-12 w-12 text-muted-foreground/50 animate-pulse relative z-20" />
|
<Coffee className="h-12 w-12 text-muted-foreground/50 animate-pulse relative z-20" />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -22,8 +22,9 @@ interface CoffeeShop {
|
|||||||
interface MapLoaderProps {
|
interface MapLoaderProps {
|
||||||
shops: CoffeeShop[];
|
shops: CoffeeShop[];
|
||||||
onShopSelect: (shop: CoffeeShop) => void;
|
onShopSelect: (shop: CoffeeShop) => void;
|
||||||
|
selectedShop: CoffeeShop | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function MapLoader({ shops, onShopSelect }: MapLoaderProps) {
|
export default function MapLoader({ shops, onShopSelect, selectedShop }: MapLoaderProps) {
|
||||||
return <Map shops={shops} onShopSelect={onShopSelect} />;
|
return <Map shops={shops} onShopSelect={onShopSelect} selectedShop={selectedShop} />;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
import { postRouter } from "~/server/api/routers/post";
|
|
||||||
import { createCallerFactory, createTRPCRouter } from "~/server/api/trpc";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is the primary router for your server.
|
|
||||||
*
|
|
||||||
* All routers added in /api/routers should be manually added here.
|
|
||||||
*/
|
|
||||||
export const appRouter = createTRPCRouter({
|
|
||||||
post: postRouter,
|
|
||||||
});
|
|
||||||
|
|
||||||
// export type definition of API
|
|
||||||
export type AppRouter = typeof appRouter;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a server-side caller for the tRPC API.
|
|
||||||
* @example
|
|
||||||
* const trpc = createCaller(createContext);
|
|
||||||
* const res = await trpc.post.all();
|
|
||||||
* ^? Post[]
|
|
||||||
*/
|
|
||||||
export const createCaller = createCallerFactory(appRouter);
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
import { z } from "zod";
|
|
||||||
|
|
||||||
import { createTRPCRouter, publicProcedure } from "~/server/api/trpc";
|
|
||||||
|
|
||||||
// Mocked DB
|
|
||||||
interface Post {
|
|
||||||
id: number;
|
|
||||||
name: string;
|
|
||||||
}
|
|
||||||
const posts: Post[] = [
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
name: "Hello World",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export const postRouter = createTRPCRouter({
|
|
||||||
hello: publicProcedure
|
|
||||||
.input(z.object({ text: z.string() }))
|
|
||||||
.query(({ input }) => {
|
|
||||||
return {
|
|
||||||
greeting: `Hello ${input.text}`,
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
|
|
||||||
create: publicProcedure
|
|
||||||
.input(z.object({ name: z.string().min(1) }))
|
|
||||||
.mutation(async ({ input }) => {
|
|
||||||
const post: Post = {
|
|
||||||
id: posts.length + 1,
|
|
||||||
name: input.name,
|
|
||||||
};
|
|
||||||
posts.push(post);
|
|
||||||
return post;
|
|
||||||
}),
|
|
||||||
|
|
||||||
getLatest: publicProcedure.query(() => {
|
|
||||||
return posts.at(-1) ?? null;
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
@@ -1,99 +0,0 @@
|
|||||||
/**
|
|
||||||
* YOU PROBABLY DON'T NEED TO EDIT THIS FILE, UNLESS:
|
|
||||||
* 1. You want to modify request context (see Part 1).
|
|
||||||
* 2. You want to create a new middleware or type of procedure (see Part 3).
|
|
||||||
*
|
|
||||||
* TL;DR - This is where all the tRPC server stuff is created and plugged in. The pieces you will
|
|
||||||
* need to use are documented accordingly near the end.
|
|
||||||
*/
|
|
||||||
import { initTRPC } from "@trpc/server";
|
|
||||||
import superjson from "superjson";
|
|
||||||
import { ZodError } from "zod";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 1. CONTEXT
|
|
||||||
*
|
|
||||||
* This section defines the "contexts" that are available in the backend API.
|
|
||||||
*
|
|
||||||
* These allow you to access things when processing a request, like the database, the session, etc.
|
|
||||||
*
|
|
||||||
* This helper generates the "internals" for a tRPC context. The API handler and RSC clients each
|
|
||||||
* wrap this and provides the required context.
|
|
||||||
*
|
|
||||||
* @see https://trpc.io/docs/server/context
|
|
||||||
*/
|
|
||||||
export const createTRPCContext = async (opts: { headers: Headers }) => {
|
|
||||||
return {
|
|
||||||
...opts,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 2. INITIALIZATION
|
|
||||||
*
|
|
||||||
* This is where the tRPC API is initialized, connecting the context and transformer. We also parse
|
|
||||||
* ZodErrors so that you get typesafety on the frontend if your procedure fails due to validation
|
|
||||||
* errors on the backend.
|
|
||||||
*/
|
|
||||||
const t = initTRPC.context<typeof createTRPCContext>().create({
|
|
||||||
transformer: superjson,
|
|
||||||
errorFormatter({ shape, error }) {
|
|
||||||
return {
|
|
||||||
...shape,
|
|
||||||
data: {
|
|
||||||
...shape.data,
|
|
||||||
zodError:
|
|
||||||
error.cause instanceof ZodError ? error.cause.flatten() : null,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a server-side caller.
|
|
||||||
*
|
|
||||||
* @see https://trpc.io/docs/server/server-side-calls
|
|
||||||
*/
|
|
||||||
export const createCallerFactory = t.createCallerFactory;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 3. ROUTER & PROCEDURE (THE IMPORTANT BIT)
|
|
||||||
*
|
|
||||||
* These are the pieces you use to build your tRPC API. You should import these a lot in the
|
|
||||||
* "/src/server/api/routers" directory.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is how you create new routers and sub-routers in your tRPC API.
|
|
||||||
*
|
|
||||||
* @see https://trpc.io/docs/router
|
|
||||||
*/
|
|
||||||
export const createTRPCRouter = t.router;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Middleware for timing procedure execution and adding an artificial delay.
|
|
||||||
*
|
|
||||||
* You can remove this if you don't want to do it.
|
|
||||||
*/
|
|
||||||
const timingMiddleware = t.middleware(async ({ next }) => {
|
|
||||||
if (t._config.isDev) {
|
|
||||||
// artificial delay in dev 100-500ms
|
|
||||||
const waitMs = Math.floor(Math.random() * 400) + 100;
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, waitMs));
|
|
||||||
}
|
|
||||||
const result = await next();
|
|
||||||
if (t._config.isDev) {
|
|
||||||
// const durationMs = Date.now() - start;
|
|
||||||
// console.log(`[TRPC] ${path} took ${durationMs}ms to execute`);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Public (unauthenticated) procedure
|
|
||||||
*
|
|
||||||
* This is the base piece you use to build new queries and mutations on your tRPC API. It does not
|
|
||||||
* guarantee that a user querying is authorized, but you can still access user session data if they
|
|
||||||
* are logged in.
|
|
||||||
*/
|
|
||||||
export const publicProcedure = t.procedure.use(timingMiddleware);
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
import {
|
|
||||||
defaultShouldDehydrateQuery,
|
|
||||||
QueryClient,
|
|
||||||
} from "@tanstack/react-query";
|
|
||||||
import SuperJSON from "superjson";
|
|
||||||
|
|
||||||
export const createQueryClient = () =>
|
|
||||||
new QueryClient({
|
|
||||||
defaultOptions: {
|
|
||||||
queries: {
|
|
||||||
// With SSR, we usually want to set some default staleTime
|
|
||||||
// above 0 to avoid refetching immediately on the client
|
|
||||||
staleTime: 30 * 1000,
|
|
||||||
},
|
|
||||||
dehydrate: {
|
|
||||||
serializeData: SuperJSON.serialize,
|
|
||||||
shouldDehydrateQuery: (query) =>
|
|
||||||
defaultShouldDehydrateQuery(query) ||
|
|
||||||
query.state.status === "pending",
|
|
||||||
},
|
|
||||||
hydrate: {
|
|
||||||
deserializeData: SuperJSON.deserialize,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
"use client";
|
|
||||||
|
|
||||||
import { QueryClientProvider, type QueryClient } from "@tanstack/react-query";
|
|
||||||
import { httpBatchStreamLink, loggerLink } from "@trpc/client";
|
|
||||||
import { createTRPCReact } from "@trpc/react-query";
|
|
||||||
import { type inferRouterInputs, type inferRouterOutputs } from "@trpc/server";
|
|
||||||
import { useState } from "react";
|
|
||||||
import SuperJSON from "superjson";
|
|
||||||
|
|
||||||
import { type AppRouter } from "~/server/api/root";
|
|
||||||
import { createQueryClient } from "./query-client";
|
|
||||||
|
|
||||||
let clientQueryClientSingleton: QueryClient | undefined = undefined;
|
|
||||||
const getQueryClient = () => {
|
|
||||||
if (typeof window === "undefined") {
|
|
||||||
// Server: always make a new query client
|
|
||||||
return createQueryClient();
|
|
||||||
}
|
|
||||||
// Browser: use singleton pattern to keep the same query client
|
|
||||||
clientQueryClientSingleton ??= createQueryClient();
|
|
||||||
|
|
||||||
return clientQueryClientSingleton;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const api = createTRPCReact<AppRouter>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Inference helper for inputs.
|
|
||||||
*
|
|
||||||
* @example type HelloInput = RouterInputs['example']['hello']
|
|
||||||
*/
|
|
||||||
export type RouterInputs = inferRouterInputs<AppRouter>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Inference helper for outputs.
|
|
||||||
*
|
|
||||||
* @example type HelloOutput = RouterOutputs['example']['hello']
|
|
||||||
*/
|
|
||||||
export type RouterOutputs = inferRouterOutputs<AppRouter>;
|
|
||||||
|
|
||||||
export function TRPCReactProvider(props: { children: React.ReactNode }) {
|
|
||||||
const queryClient = getQueryClient();
|
|
||||||
|
|
||||||
const [trpcClient] = useState(() =>
|
|
||||||
api.createClient({
|
|
||||||
links: [
|
|
||||||
loggerLink({
|
|
||||||
enabled: (op) =>
|
|
||||||
process.env.NODE_ENV === "development" ||
|
|
||||||
(op.direction === "down" && op.result instanceof Error),
|
|
||||||
}),
|
|
||||||
httpBatchStreamLink({
|
|
||||||
transformer: SuperJSON,
|
|
||||||
url: getBaseUrl() + "/api/trpc",
|
|
||||||
headers: () => {
|
|
||||||
const headers = new Headers();
|
|
||||||
headers.set("x-trpc-source", "nextjs-react");
|
|
||||||
return headers;
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<QueryClientProvider client={queryClient}>
|
|
||||||
<api.Provider client={trpcClient} queryClient={queryClient}>
|
|
||||||
{props.children}
|
|
||||||
</api.Provider>
|
|
||||||
</QueryClientProvider>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getBaseUrl() {
|
|
||||||
if (typeof window !== "undefined") return window.location.origin;
|
|
||||||
if (process.env.VERCEL_URL) return `https://${process.env.VERCEL_URL}`;
|
|
||||||
return `http://localhost:${process.env.PORT ?? 3000}`;
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
import "server-only";
|
|
||||||
|
|
||||||
import { createHydrationHelpers } from "@trpc/react-query/rsc";
|
|
||||||
import { headers } from "next/headers";
|
|
||||||
import { cache } from "react";
|
|
||||||
|
|
||||||
import { createCaller, type AppRouter } from "~/server/api/root";
|
|
||||||
import { createTRPCContext } from "~/server/api/trpc";
|
|
||||||
import { createQueryClient } from "./query-client";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This wraps the `createTRPCContext` helper and provides the required context for the tRPC API when
|
|
||||||
* handling a tRPC call from a React Server Component.
|
|
||||||
*/
|
|
||||||
const createContext = cache(async () => {
|
|
||||||
const heads = new Headers(await headers());
|
|
||||||
heads.set("x-trpc-source", "rsc");
|
|
||||||
|
|
||||||
return createTRPCContext({
|
|
||||||
headers: heads,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const getQueryClient = cache(createQueryClient);
|
|
||||||
const caller = createCaller(createContext);
|
|
||||||
|
|
||||||
export const { trpc: api, HydrateClient } = createHydrationHelpers<AppRouter>(
|
|
||||||
caller,
|
|
||||||
getQueryClient,
|
|
||||||
);
|
|
||||||