stevekrouse-cron_client_react_fork.web.val.run
Readme

CronGPT

This is a minisite to help you create cron expressions, particularly for crons on Val Town. It was inspired by Cron Prompt, but also does the timezone conversion from wherever you are to UTC (typically the server timezone).

Tech

  • Hono for routing (GET / and POST /compile.)
  • Hono JSX
  • HTML (probably overcomplicates things; should remove)
  • @stevekrouse/openai, which is a light wrapper around @std/openai

TODO

  • simplify by removing HTMX (try doing the form as a GET request, manual JS script, or client side react)
  • make the timezone picker better (fewer options, searchable)
  • add a copy button?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
/** @jsxImportSource https://esm.sh/react@18.2.0 */
import cronstrue from "https://esm.sh/cronstrue";
import React, { useState } from "https://esm.sh/react@18.2.0";
import { chat } from "https://esm.town/v/stevekrouse/openai?v=19";
import react_http from "https://esm.town/v/stevekrouse/react_http?v=6";
export default function(req: Request) {
const url = new URL(req.url);
if (req.method === "GET" && url.pathname === "/") {
return react_http({
component: App,
sourceURL: import.meta.url,
head: `<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script src="https://cdn.tailwindcss.com"></script>
<title>CronGPT</title>`,
});
} else if (req.method === "POST" && url.pathname === "/compile") {
return compile(req);
}
return new Response("Not found", { status: 404 });
}
export function App() {
const browserTZ = Intl.DateTimeFormat().resolvedOptions().timeZone;
const [loading, setLoading] = useState(false);
const [cron, setCron] = useState("0 16 * * 1-5");
const onSubmit: React.FormEventHandler<HTMLFormElement> = async function(e) {
e.preventDefault();
setLoading(true);
const cron = await fetch("/compile", {
method: "POST",
body: new FormData(e.target as HTMLFormElement),
}).then((r) => r.json());
setCron(cron);
setLoading(false);
};
return (
<div className="flex p-6 mt-4 flex-col space-y-12 max-w-2xl mx-auto">
<div className="flex flex-col text-center space-y-2">
<h1 className="text-3xl font-bold">CronGPT</h1>
<p className="text-lg">Generate cron expressions with ChatGPT</p>
</div>
<form className="flex flex-col space-y-4" onSubmit={onSubmit}>
<div className="flex flex-col">
<label>Natural language description</label>
<input
name="description"
defaultValue="On weekdays at noon"
required
className="border-2 rounded-lg p-2 text-lg text-center"
/>
</div>
<div className="flex flex-col">
<label>Timezone</label>
<select name="timezone" className=" border-2 rounded-lg text-lg p-2 text-center">
{Intl.supportedValuesOf("timeZone").map((tz) => <option value={tz} selected={tz === browserTZ}>{tz}
</option>)}
</select>
</div>
<button
className="bg-sky-500 hover:bg-sky-700 text-white font-bold py-2 px-4 rounded disabled:bg-gray-500"
disabled={loading}
>
{loading ? "Loading.." : "Compile"}
</button>
</form>
<Cron cron={cron} timezone="America/New_York" />
<div className="text-gray-500 text-center flex flex-col space-y-2">
<div>
Need a place to run cron jobs? Try <Link href="https://val.town">Val Town</Link>
</div>{" "}
<div>
<Link href="https://www.val.town/v/stevekrouse/cron">View source</Link>
{" | "} Inspired by <Link href="https://cronprompt.com/">Cron Prompt</Link>
</div>
</div>
</div>
);
}
const Link = ({ href, children }: { href: string; children?: any }) => (
<a href={href} className="text-blue-500 hover:text-blue-700" target="_blank" rel="noopener noreferrer">{children}</a>
);
function Cron({ cron, timezone }) {
let translation;
try {
translation = cronstrue.toString(cron, { tzOffset: getOffset(timezone), use24HourTimeFormat: false });
} catch (e) {
translation = "Translation error: " + e.message;
}
return (
<div id="cron" className="flex flex-col space-y-4">
<pre className="font-mono text-2xl flex flex-row justify-between w-full border-2 p-2 bg-gray-200">
<div></div>
<div className="flex">
<div className="font-bold select-all">{cron}</div>
</div>
<div className="text-gray-400 font-bold mr-2 select-none">UTC</div>
Val Town is a social website to write and deploy JavaScript.
Build APIs and schedule functions from your browser.
Comments
Nobody has commented on this val yet: be the first!
May 10, 2024