diff --git a/backend/package-lock.json b/backend/package-lock.json index 8c99059..1d750b1 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -15,9 +15,11 @@ "fastify": "^5.2.1", "fastify-cors": "^6.0.3", "fastify-multipart": "^5.3.1", - "libreoffice-convert": "^1.6.0" + "libreoffice-convert": "^1.6.0", + "luxon": "^3.5.0" }, "devDependencies": { + "@types/luxon": "^3.4.2", "@types/node": "^22.13.4", "prettier": "3.5.1", "ts-node": "^10.9.2", @@ -263,6 +265,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/luxon": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.4.2.tgz", + "integrity": "sha512-TifLZlFudklWlMBfhubvgqTXRzLDI5pCbGa4P8a3wPyUQSW+1xQ5eDsreP9DWHX3tjq1ke96uYG/nwundroWcA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/node": { "version": "22.13.4", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.4.tgz", @@ -682,6 +691,15 @@ "set-cookie-parser": "^2.6.0" } }, + "node_modules/luxon": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.5.0.tgz", + "integrity": "sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ==", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, "node_modules/make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", diff --git a/backend/package.json b/backend/package.json index 671e0b5..9f959c8 100644 --- a/backend/package.json +++ b/backend/package.json @@ -18,9 +18,11 @@ "fastify": "^5.2.1", "fastify-cors": "^6.0.3", "fastify-multipart": "^5.3.1", - "libreoffice-convert": "^1.6.0" + "libreoffice-convert": "^1.6.0", + "luxon": "^3.5.0" }, "devDependencies": { + "@types/luxon": "^3.4.2", "@types/node": "^22.13.4", "prettier": "3.5.1", "ts-node": "^10.9.2", diff --git a/backend/server.ts b/backend/server.ts index bbbf6ba..607a74c 100644 --- a/backend/server.ts +++ b/backend/server.ts @@ -5,6 +5,7 @@ import { libreConvert } from "./src/routes/libreconvert.route"; import { colorConvert } from "./src/routes/colorconvert.route"; import { passwordGenerate } from "./src/routes/passwordgenerate.route"; import { regexTest } from "./src/routes/regextest.route"; +import { tmzConvert } from "./src/routes/tmzconvert.route"; const app = Fastify({ logger: true }); @@ -19,6 +20,7 @@ app.register(libreConvert); app.register(colorConvert); app.register(passwordGenerate); app.register(regexTest); +app.register(tmzConvert); const PORT = process.env.PORT || 4000; app.listen({ port: Number(PORT), host: "0.0.0.0" }, () => { diff --git a/backend/src/routes/tmzconvert.route.ts b/backend/src/routes/tmzconvert.route.ts new file mode 100644 index 0000000..4b2525d --- /dev/null +++ b/backend/src/routes/tmzconvert.route.ts @@ -0,0 +1,45 @@ +import { FastifyInstance, FastifyReply, FastifyRequest } from "fastify"; +import { DateTime } from "luxon"; + +interface RequestBody { + time: string; + fromZone: string; + toZone: string; +} + +export async function tmzConvert(app: FastifyInstance) { + app.post( + "/api/tmz-convert", + async ( + request: FastifyRequest<{ Body: RequestBody }>, + reply: FastifyReply, + ) => { + const data = request.body; + const time = data.time; + const fromZone = data.fromZone; + const toZone = data.toZone; + + try { + if (!time || !fromZone || !toZone) { + return reply.status(400).send({ error: "Missing parameters" }); + } + + const dateTime = DateTime.fromISO(time, { zone: fromZone }); + + if (!dateTime.isValid) { + return reply.status(400).send({ error: "Invalid date or time zone" }); + } + + const convertedTime = dateTime.setZone(toZone).toISO(); + + return reply + .header("Content-Type", "text/plain") + .status(200) + .send(convertedTime); + } catch (error) { + console.error("Conversion error:", error); + reply.status(500).send({ error: "Error converting time" }); + } + }, + ); +} diff --git a/frontend/src/app/tmz-converter/layout.tsx b/frontend/src/app/tmz-converter/layout.tsx new file mode 100644 index 0000000..677474a --- /dev/null +++ b/frontend/src/app/tmz-converter/layout.tsx @@ -0,0 +1,20 @@ +import React from "react"; +import type { Metadata } from "next"; +import { toolLinks } from "@/constants"; + +export const metadata: Metadata = { + title: toolLinks[1].title, + description: "Converter for timezones!", +}; + +export default function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + return ( + + {children} + + ); +} diff --git a/frontend/src/app/tmz-converter/page.tsx b/frontend/src/app/tmz-converter/page.tsx new file mode 100644 index 0000000..b138d7c --- /dev/null +++ b/frontend/src/app/tmz-converter/page.tsx @@ -0,0 +1,103 @@ +"use client"; +import React, { useState } from "react"; +import Navbar from "../../components/Navbar"; +import Footer from "../../components/Footer"; +import Button from "../../components/Button"; + +export default function DocConverter() { + const [loading, setLoading] = useState(false); + const [convertedTMZ, setConvertedTMZ] = useState(""); + + const convertTMZ = async () => { + setLoading(true); + + const time = (document.getElementById("time") as HTMLInputElement).value; + const fromZone = (document.getElementById("fromZone") as HTMLInputElement) + .value; + const toZone = (document.getElementById("toZone") as HTMLInputElement) + .value; + + try { + const response = await fetch( + process.env.backend_url + "/api/tmz-convert", + { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + time: time, + fromZone: fromZone, + toZone: toZone, + }), + }, + ); + if (!response.ok) { + return new Error(`Error: ${response.statusText}`); + } + const output: string = await response.text(); + setConvertedTMZ(output); + } catch (error) { + console.error("Error while converting:", error); + alert("Error while converting"); + } finally { + setLoading(false); + } + }; + + return ( +
+ +
+

tmz-converter

+

Example:

+
+

Time: 2025-02-20T15:30:00

+

From TMZ: America/New_York

+

To TMZ: Europe/Berlin

+
+

+ Output: 2025-02-20T21:30:00.000+01:00 +

+
+ + + +
+
+
+
+ + {convertedTMZ} + +
+
+
+ ); +} diff --git a/frontend/src/constants/index.ts b/frontend/src/constants/index.ts index f1b1eba..13db40f 100644 --- a/frontend/src/constants/index.ts +++ b/frontend/src/constants/index.ts @@ -4,8 +4,8 @@ export const toolLinks = [ link: "/doc-converter", }, { - title: "img-converter", - link: "/img-converter", + title: "tmz-converter", + link: "/tmz-converter", }, { title: "rgb-to-hex", @@ -32,8 +32,8 @@ export const toolLinks = [ link: "/word-counter", }, { - title: "pomodoro-timer", - link: "/pomodoro-timer", + title: "video-to-audio", + link: "/video-to-audio", }, ];