class Task < ApplicationRecord
POSITION_GAP = 1024
belongs_to :board
scope :ordered, -> { order(:position) }
import { Controller } from "@hotwired/stimulus";
export default class extends Controller {
static targets = ["field"];
connect() {
const withTimeout = (promise, timeoutMs, name) => {
let timer;
const timeout = new Promise((_, reject) => {
timer = setTimeout(
() => reject(new Error(`check '${name}' timed out after ${timeoutMs}ms`)),
timeoutMs
function encodeCursor(row) {
if (!row) return null;
const payload = JSON.stringify({ t: row.created_at, id: row.id });
return Buffer.from(payload, 'utf8').toString('base64url');
}
module FlashHelper
FLASH_CLASSES = {
"notice" => "flash--success",
"alert" => "flash--error",
"error" => "flash--error"
}.freeze
class ApiError extends Error {
constructor(statusCode, message, code) {
super(message);
this.name = 'ApiError';
this.statusCode = statusCode;
this.code = code || null;
class BatchLoader {
constructor(batchFn, { cacheKeyFn = (k) => k } = {}) {
this.batchFn = batchFn;
this.cacheKeyFn = cacheKeyFn;
this.cache = new Map();
this.queue = [];
import { Controller } from "@hotwired/stimulus"
import { NotificationsApi } from "notifications_api"
export default class extends Controller {
static targets = ["notification"]
static values = {
module TimeAgoHelper
def time_ago_tag(time, locale: I18n.locale, **options)
return if time.blank?
time = time.to_time
fallback = l(time, format: :short)
const express = require('express');
const multer = require('multer');
const os = require('os');
const fs = require('fs/promises');
const { parseCsvStream } = require('./csvStreamParser');
const fs = require('fs');
const readline = require('readline');
async function* streamCsvRows(filePath) {
const stream = fs.createReadStream(filePath, { encoding: 'utf8' });
const rl = readline.createInterface({ input: stream, crlfDelay: Infinity });
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
static targets = ["field"]
validateField(event) {