Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added .github/assets/images/banner.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed .github/assets/images/hero.jpg
Binary file not shown.
16 changes: 0 additions & 16 deletions .github/assets/images/logo.svg

This file was deleted.

8 changes: 8 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@ Monorepo for Timeful, a group availability/scheduling app.

Internal identifiers (Go module `schej.it/server`, Mongo DB `schej-it`, prod email "Schej.it") still use the old name — leave them alone unless rebranding is the explicit task.

## Branding (2bz2fill fork)

- User-visible name is **2bz2fill**; it is a fork of Timeful and keeps a "Based on Timeful" credit plus a source-code link in the footer (AGPL §13).
- No domain yet: the server's public address comes from `BASE_URL` (placeholder `https://2bz2fill.example`); the sign-in email sender from `EMAIL_FROM`. Frontend links to the site use `window.location.origin`, never a hard-coded domain.
- Never point users or data at Timeful-owned services: their analytics (PostHog host, GTM container), PayPal, feedback form, Discord/Reddit/X, blog, or `betterwhen2meet.com`. Analytics are opt-in via `VUE_APP_POSTHOG_API_KEY` / `VUE_APP_GTM_ID`.
- `PrivacyPolicy.vue` is written from what the code actually stores and sends. When a change adds data collection, a third party, analytics or emails, update the policy in the same change.
- The logo is drawn in code (`BrandMark.vue` + DM Sans wordmark in `Logo.vue`); `public/favicon.svg` must match `BrandMark.vue`.

## Common commands

### Frontend (`cd frontend`)
Expand Down
70 changes: 41 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,51 +1,63 @@
<div align="center">

<img src="./.github/assets/images/logo.svg" width="200px" alt="Timeful logo" />
<img src="./.github/assets/images/banner.png" alt="2bz2fill: too busy to fill out the grid? Let your calendar do it." />

</div>
<br />
<div align="center">

[![License: AGPL v3](https://img.shields.io/badge/License-AGPL_v3-orange.svg)](https://www.gnu.org/licenses/agpl-3.0)
[![Donate](https://img.shields.io/badge/-Donate%20with%20Paypal-blue?logo=paypal)](https://www.paypal.com/donate/?hosted_button_id=KWCH6LGJCP6E6)
[![X (formerly Twitter) Follow](https://img.shields.io/twitter/follow/timeful_app?label=%40timeful_app&labelColor=white)](https://x.com/timeful_app)
[![Discord](https://img.shields.io/badge/-Join%20Discord-7289DA?logo=discord&logoColor=white)](https://discord.gg/v6raNqYxx3)
[![Subreddit subscribers](https://img.shields.io/reddit/subreddit-subscribers/schej?label=join%20r%2Fschej)](https://www.reddit.com/r/schej/)

</div>

<img src="./.github/assets/images/hero.jpg" alt="Timeful hero" />
**2bz2fill** is a group availability poll for people who are too busy to fill out a long
When2meet-style grid. Every feature is judged by one question: does it make responding take less
effort?

It is a fork of [Timeful](https://github.com/schej-it/timeful.app) (formerly Schej), an open-source
scheduling app by Jonathan Liu and contributors. Thank you to them: the poll grid, calendar
integrations, time zone handling and most of the code here are theirs.

Timeful is a scheduling platform helps you find the best time for a group to meet. It is a free availability poll that is easy to use and integrates with your calendar.
Built with [Vue 2](https://github.com/vuejs/vue), [MongoDB](https://github.com/mongodb/mongo),
[Go](https://github.com/golang/go), and [TailwindCSS](https://github.com/tailwindlabs/tailwindcss).

Hosted version of the site: https://timeful.app
## How it differs from Timeful

Built with [Vue 2](https://github.com/vuejs/vue), [MongoDB](https://github.com/mongodb/mongo), [Go](https://github.com/golang/go), and [TailwindCSS](https://github.com/tailwindlabs/tailwindcss)
Timeful is a general scheduling poll. 2bz2fill keeps its features and focuses on cutting the work
of responding. Planned, in order:

## Demo
1. Upload an `.ics` file to fill the grid from any calendar app, with no account or sign-in.
2. Save "my usual week" once and have every new poll arrive pre-filled from it.
3. Fast manual tools: mark busy times instead of free ones, fill or clear a whole day, copy a day,
reuse your last response.
4. Pre-filled responses you only need to check and confirm.
5. A grid that works well on a phone.

[![demo video](http://markdown-videos-api.jorgenkh.no/youtube/vFkBC8BrkOk)](https://www.youtube.com/watch?v=vFkBC8BrkOk)
The Timeful-specific parts (their branding, blog, socials, analytics, donation links and the
When2meet converter, which runs on Timeful's own servers) have been removed.

## Features
## Features inherited from Timeful

- See when everybody's availability overlaps
- Easily specify date + time ranges to meet between
- Google calendar, Outlook, Apple calendar integration
- Date and time ranges, or days of the week
- Autofill from Google Calendar, Outlook, Apple Calendar or an ICS feed URL
- "Available" vs. "If needed" times
- Determine when a subset of people are available
- Schedule across different time zones
- Email notifications + reminders
- Duplicating polls
- Availability groups - stay up to date with people's real-time calendar availability
- Export availability as CSV
- Only show responses to event creator
- See when a subset of people are available
- Time zone support
- Email notifications and reminders
- Duplicating polls, availability groups, CSV export
- Only show responses to the event creator
- Import a poll (with its responses) from a 2bz2fill or Timeful link

## Plugin API

Read these docs to design your own browser plugins to get + set availability on Timeful events programmatically!

[Plugin API Docs](./PLUGIN_API_README.md)
Browser plugins can get and set availability on a poll programmatically. See the
[Plugin API docs](./PLUGIN_API_README.md).

## Self-hosting

See the [Deployment Guide](./DEPLOYMENT.md) for Docker Compose and NixOS setup instructions.
See the [Deployment Guide](./DEPLOYMENT.md) for Docker Compose and NixOS setup. Set `BASE_URL` in
`server/.env` to the site's public address; until it is set, emails link to the placeholder
`https://2bz2fill.example`. Analytics are off unless you set your own PostHog key or Google Tag
Manager ID in `frontend/.env` (see `frontend/.env.template`).

## License

[GNU AGPL v3](./LICENSE), the same as Timeful. If you run a modified copy as a public website, you
must offer its source code to your users; the site footer links to this repository for that reason.
8 changes: 7 additions & 1 deletion frontend/.env.template
Original file line number Diff line number Diff line change
@@ -1 +1,7 @@
VUE_APP_POSTHOG_API_KEY=?
# Google OAuth client ID (same value as CLIENT_ID in server/.env). Required for Google sign-in.
VUE_APP_GOOGLE_CLIENT_ID=
VUE_APP_POSTHOG_API_KEY=
# Optional. Defaults to PostHog Cloud (US).
VUE_APP_POSTHOG_HOST=
# Optional Google Tag Manager container ID, e.g. GTM-XXXXXXX
VUE_APP_GTM_ID=
Binary file modified frontend/public/favicon-16x16.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified frontend/public/favicon-32x32.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified frontend/public/favicon.ico
Binary file not shown.
1 change: 1 addition & 0 deletions frontend/public/favicon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified frontend/public/img/ogImage.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed frontend/public/img/when2meetOgImage2.png
Binary file not shown.
40 changes: 5 additions & 35 deletions frontend/public/index.html
Original file line number Diff line number Diff line change
@@ -1,42 +1,22 @@
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Google Tag Manager -->
<script>
;(function (w, d, s, l, i) {
w[l] = w[l] || []
w[l].push({ "gtm.start": new Date().getTime(), event: "gtm.js" })
var f = d.getElementsByTagName(s)[0],
j = d.createElement(s),
dl = l != "dataLayer" ? "&l=" + l : ""
j.async = true
j.src = "https://www.googletagmanager.com/gtm.js?id=" + i + dl
f.parentNode.insertBefore(j, f)
})(window, document, "script", "dataLayer", "GTM-M677X6V")
</script>
<!-- End Google Tag Manager -->

<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
<link rel="icon" href="<%= BASE_URL %>favicon.ico" />
<link rel="icon" href="<%= BASE_URL %>favicon.ico" sizes="32x32" />
<link rel="icon" href="<%= BASE_URL %>favicon.svg" type="image/svg+xml" />
<!-- Chrome, Firefox OS and Opera -->
<meta name="theme-color" content="#FFFFFF" />
<!-- Windows Phone -->
<meta name="msapplication-navbutton-color" content="#FFFFFF" />
<!-- iOS Safari -->
<meta name="apple-mobile-web-app-status-bar-style" content="#FFFFFF" />
<div style="display:none">
{{ $defaultTitle := "Timeful - Find a time to meet" }}
{{ $defaultDescription := "Timeful helps you find the best time for a group to meet. It is a free availability poll that is easy to use and integrates with your calendar." }}
{{ $defaultTitle := "2bz2fill - Find a time to meet" }}
{{ $defaultDescription := "2bz2fill is a group availability poll for people too busy to fill out a long grid. Autofill your availability from your calendar, check it, and you're done." }}
{{ $defaultOgImage := "/img/ogImage.png" }}
</div>
<!-- <title>Timeful - Find a time to meet</title>
<meta name="description" content="Timeful helps you find the best time for a group to meet. It is a free availability poll that is easy to use and integrates with your calendar." />
<meta property="og:title" content="Timeful - Find a time to meet" />
<meta property="og:description" content="Timeful helps you find the best time for a group to meet. It is a free availability poll that is easy to use and integrates with your calendar." />
<meta property="og:image" content="/img/ogImage.png" />
<meta property="og:image:secure_url" content="/img/ogImage.png" /> -->
<title>{{ or .title $defaultTitle }}</title>
<meta
name="description"
Expand All @@ -58,19 +38,9 @@
/>
</head>
<body>
<!-- Google Tag Manager (noscript) -->
<noscript
><iframe
src="https://www.googletagmanager.com/ns.html?id=GTM-M677X6V"
height="0"
width="0"
style="display: none; visibility: hidden"
></iframe
></noscript>
<!-- End Google Tag Manager (noscript) -->
<noscript>
<strong
>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work
>We're sorry but 2bz2fill doesn't work
properly without JavaScript enabled. Please enable it to
continue.</strong
>
Expand Down
32 changes: 1 addition & 31 deletions frontend/src/App.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
<template>
<v-app>
<DiscordBanner />
<AutoSnackbar color="error" :text="error" />
<AutoSnackbar color="tw-bg-blue" :text="info" />
<SignInNotSupportedDialog v-model="webviewDialog" />
Expand All @@ -16,7 +15,6 @@
:no-tabs="newDialogOptions.eventOnly"
:folder-id="newDialogOptions.folderId"
/>
<UpvoteRedditSnackbar />
<div
v-if="showHeader"
class="tw-fixed tw-z-40 tw-h-14 tw-w-screen tw-bg-white sm:tw-h-16"
Expand All @@ -26,7 +24,7 @@
class="tw-relative tw-m-auto tw-flex tw-h-full tw-max-w-6xl tw-items-center tw-justify-center tw-px-4"
>
<router-link :to="{ name: 'home' }">
<Logo type="timeful" />
<Logo />
</router-link>
<v-expand-x-transition>
<span
Expand All @@ -47,24 +45,6 @@
>
Create an event
</v-btn>
<v-btn
v-if="showFeedbackBtn"
id="feedback-btn"
text
href="https://forms.gle/A96i4TTWeKgH3P1W6"
target="_blank"
@click="trackFeedbackClick"
>
Give feedback
</v-btn>
<v-btn
v-if="!isPhone"
text
href="https://www.paypal.com/donate/?hosted_button_id=KWCH6LGJCP6E6"
target="_blank"
>
Donate
</v-btn>
<v-btn
v-if="$route.name === 'home' && !isPhone"
color="primary"
Expand Down Expand Up @@ -238,12 +218,10 @@ import { authTypes, calendarTypes, eventTypes } from "@/constants"
import AutoSnackbar from "@/components/AutoSnackbar"
import AuthUserMenu from "@/components/AuthUserMenu.vue"
import SignInNotSupportedDialog from "@/components/SignInNotSupportedDialog.vue"
import UpvoteRedditSnackbar from "@/components/UpvoteRedditSnackbar.vue"
import Logo from "@/components/Logo.vue"
import isWebview from "is-ua-webview"
import NewDialog from "./components/NewDialog.vue"
import SignInDialog from "@/components/SignInDialog.vue"
import DiscordBanner from "@/components/DiscordBanner.vue"

export default {
name: "App",
Expand All @@ -259,10 +237,8 @@ export default {
AuthUserMenu,
SignInNotSupportedDialog,
NewDialog,
UpvoteRedditSnackbar,
Logo,
SignInDialog,
DiscordBanner,
},

data: () => ({
Expand All @@ -287,9 +263,6 @@ export default {
this.$route.name !== "privacy-policy"
)
},
showFeedbackBtn() {
return !this.isPhone || this.$route.name === "home"
},
routerViewClass() {
let c = ""
if (this.showHeader) {
Expand Down Expand Up @@ -383,9 +356,6 @@ export default {
// this.setSignUpFormEnabled(this.$posthog.isFeatureEnabled("sign-up-form"))
this.setFeatureFlagsLoaded(true)
},
trackFeedbackClick() {
this.$posthog.capture("give_feedback_button_clicked")
},
},

async created() {
Expand Down
Binary file removed frontend/src/assets/audio/schej_pronunciation.mp3
Binary file not shown.
Binary file removed frontend/src/assets/doodles/boba/0.jpg
Binary file not shown.
Binary file removed frontend/src/assets/doodles/boba/1.jpg
Binary file not shown.
Binary file removed frontend/src/assets/doodles/boba/2.jpg
Binary file not shown.
Binary file removed frontend/src/assets/doodles/boba/3.jpg
Binary file not shown.
Binary file removed frontend/src/assets/doodles/boba/4.jpg
Binary file not shown.
Binary file removed frontend/src/assets/doodles/boba/5.jpg
Binary file not shown.
Binary file removed frontend/src/assets/doodles/boba/6.jpg
Binary file not shown.
Binary file removed frontend/src/assets/doodles/boba/7.jpg
Binary file not shown.
Binary file removed frontend/src/assets/doodles/pronunciation/0.jpg
Binary file not shown.
Binary file removed frontend/src/assets/doodles/pronunciation/1.jpg
Binary file not shown.
Binary file removed frontend/src/assets/doodles/pronunciation/2.jpg
Binary file not shown.
Binary file removed frontend/src/assets/doodles/pronunciation/3.jpg
Binary file not shown.
Binary file removed frontend/src/assets/doodles/pronunciation/4.jpg
Binary file not shown.
Binary file modified frontend/src/assets/img/hero.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed frontend/src/assets/schej_character.png
Binary file not shown.
Binary file removed frontend/src/assets/schej_logo.png
Binary file not shown.
1 change: 0 additions & 1 deletion frontend/src/assets/schej_logo.svg

This file was deleted.

Diff not rendered.
Binary file removed frontend/src/assets/schej_logo_with_text.png
Diff not rendered.
Loading