Changelog
Cosa c'è di nuovo.
Ogni miglioramento alla piattaforma, come si spedisce.
· kamo-shared-library
Il dominio payroll-provider dietro il motore di timecard
Aggiunge la colonna vertebrale di persistenza per collegare le schede temporali di un'organizzazione a una sistema payroll di terze parti: la connessione e la sua configurazione crittografata per-org, la sovvenzione OAuth, il link membro-provider-employee, la mappa del codice di guadagno, e i due append-only record (dispatch tentativi e problemi di sincronizzazione) che fanno una sincronizzazione spiegabile in seguito. Tenuto dentro com.kamo.z.shared.hr.timecard piuttosto che un nuovo pacchetto su scopo: un nuovo pacchetto condiviso-lib deve essere aggiunto a tre esplicito separato @EnableJpaRepositories elenca, e omettendo uno interrompe un contesto di servizio — o, in KamoInitializer, interrompe la corsa mentre ancora riporta BUILD SUCCESS. Note sulla forma: - PayrollProviderType riutilizza il vocabolario di token già documentato su E' il momento giusto. (ADP WFN, PAYCHEX, GUSTO, ...). Un secondo ortografia per lo stesso venditore avrebbe diviso la storia di esportazione senza errore ovunque. ADP RUN e QUICKBOOKS sono tenuti perché la schermata di esportazione payroll li ha offerti da quando è stato spedito, così le file dal vivo possono portarle. - I link dei dipendenti sono contrassegnati dal fornitore, non dalla connessione, quindi la commutazione il fornitore e il passaggio indietro non butta via il lavoro di mappatura. TWO vincoli unici: si ferma un membro che tiene due link, l'altro si ferma due membri che rivendicano un dipendente del fornitore — che si previene un doppio pagamento. - PayrollDispatch e PayrollSyncIssue sono WORM. Una trasmissione payroll tentativo è un evento di denaro; "abbiamo mandato, poi l'abbiamo mandato di nuovo" è il fatto necessità di riconciliazione. - Credentials live in ONE crittografato config json blob piuttosto che una colonna per campo, quindi l'aggiunta di un venditore non è un cambiamento di schema che ri-arma ogni servizio. RoleRightType guadagna MANAGE PAYROLL PROVIDER (199) — deliberatamente NON il legacy MANAGE PAYROLL (40), che non porta nulla oggi ed è già tenuto da numeri sconosciuti di membri; riutilizzarlo avrebbe consegnato il libro paga di terze parti credenziali per ogni detentore di destra di visualizzazione del tasso sul boot che lo ha spedito. PlatformOAuthProviderType ottiene i sette venditori payroll dove Kamo ne detiene uno registrazione partner (ids 11-17). credenziali di rilascio Dayforce, Workday e UKG per cliente e deliberatamente non ottenere costante: lo schermo della piattaforma rende un scheda per ogni costante, quindi uno per loro sarebbe una forma che nessun operatore potrebbe Riempilo. Corregge anche un'affermazione stante in BillingDelegationModeTest: commit 9276487 deliberatamente fatto REQUIRES APPROVAL consentire gruppi (l'esclusione ha fatto percorso di approvazione irraggiungibile) ma ha lasciato il test affermando il vecchio comportamento, quindi la suite è stata rossa sul principale da allora. Schema applicato e verificato in Yugabyte prima di questa spinta.
· kamo-internal
Un muro a "ora", e una carta che spiega un pugno cancellato
Nessuno può registrare il tempo che non hanno ancora lavorato. Dragging un bordo, scorrendo un l'intero arco, nudging con i tasti freccia, il magnete, il blocco di clic destro e il manuale aggiungere la finestra di dialogo tutti si fermano al minuto corrente sulla riga di oggi, e una il giorno futuro non accetta affatto nulla. La parte di oggi che non è accaduto è hatched, perché "non si può trascinare una pillola qui" deve essere visibile PRIMA di qualcuno controllo, non di regola. Il server ha già rifiutato un pugno inserito o corretto passato ora, che è esattamente perché il cliente aveva bisogno di questo: senza la parete la resistenza è riuscita, il pillola seduto lì guardando in scena, e l'intero salvare fallito alla fine con un messaggio su un pugno che il membro non poteva trovare. Il magnete ottiene la stessa parete. Un pugno lasciato in futuro da un dispositivo sbagliato orologio era un obiettivo a scatto legittimo, quindi un bordo potrebbe essere tirato avanti su di esso e atterrare da qualche parte il salvataggio si rifiuterebbe. E la carta di conferma ora rimane aperta quando un pugno CANCELS OUT quello prima di esso, che TimecardService ha iniziato a fare questa mattina per qualsiasi inversione all'interno di un minuto. Senza che il membro preme un pulsante, guarda lo stato scatta indietro esattamente dove è iniziato, e ragionevolmente conclude il pulsante è rotto. In che affermano che la carta cade il suo picker e la sua copia di azione, mostra una frase e un pulsante, e smette di licenziare sullo sfondo — lo stray click che così spesso segue una conferma non deve chiudere l'unica spiegazione di quello che è successo.
· kamo-internal
Dare il supporto e le finestre sociali il compositore di chat membro
Un biglietto di supporto e un DM sociale sono la stessa cosa di una chat membro — una sessione media con messaggi su di esso — ma sono stati risolti attraverso un nudo campo di testo. Un agente potrebbe essere inviato uno screenshot da un cliente e non aveva modo per inviare uno indietro: nessun pulsante di aggancio, nessun drag-drop, nessuna pasta, nessuna emoji, e un input che non crescerebbe oltre una linea. Entrambi ora rendono il Composer della chat dei membri, quindi il macchinario di upload, il vassoio di fissaggio, il pannello di trasferimento, il raccoglitore emoji e Enter-to-send sono una implementazione su tutti e tre. Ricevi e inviati allegati render in le bolle attraverso lo stesso AttachmentChip. Due cose la finestra di supporto a schede forzata all'aperto: - I progetti sono parcheggiati per conversazione (usoDraftParking). Un compositore serve diversi biglietti e i messaggi inviati a chiunque sia sullo schermo, quindi una bozza sinistra nella scatola sono parole di un cliente rivolte ad un altro. Eliminare dopo un invio prende la chiave che l'invio ha iniziato con, o le schede di commutazione mid-flight terrebbe la risposta è stata digitata a qualcun altro. - Gli allegati in fase sono tenuti anche per sessione. Una lista piana ha messo un cliente file nel vassoio di un altro e caricato lì; uploadAll ora indirizza il sessione i file sono stati in scena, qualsiasi scheda è in vista quando si atterra. Le risposte sociali sono bloccate in un file di 10 MB: un messaggio porta un singolo MediaObjSocialRef, quindi un secondo allegato potrebbe essere memorizzato e mai relèed, e 10 MB è il soffitto più stretto fornitore che inviamo attraverso. Rifiutarlo nel compositore batte perdendo silenziosamente. Un invio fallito è ora segnalato separatamente da un caricamento fallito — i file sono Su, il messaggio non è, e il progetto è ancora lì per riprovare.
· kamo-internal
Il quadrante della velocità elenca le estensioni del reparto dell'org
La scheda ha spedito sei voci inventate — Squadra di vendita su 1001, una Sala di conferenza suonare permanentemente — così dialling uno ha raggiunto ciò che è successo rispondere che estensione, e i badge BLF hanno riferito la presenza nulla è stato sottoscritto. Ora legge /api/security/departments e elenca ogni reparto che l'org ha dato un'estensione, ordinata per titolo, con il numero esterno sottostante quando uno è impostato. Caricamento, vuoto e retryable-error stati sostituire le piastrelle che non potrebbe mai essere vuoto. Il GET è org-scoped e non ha bisogno di alcun diritto, e si accende solo una volta che la scheda viene aperta per la prima volta.
· kamo-internal
Widen chat, sociale, ai-chat e comporre finestre per il formato di supporto
La finestra del biglietto di supporto si apre a 460px mentre la chat dei membri, la chat sociale, la chat AI e il compositore di posta elettronica tutto sedeva a 360-420. Stesso bacino, stesso tipo di contenuto — essi ora condividere la finestra di supporto 460px predefinito (e un pavimento 420px per il resizable quelli), quindi una fila di finestre degli strumenti aperti si dirige verso l'alto invece di scaletta.
· kamo-internal
Mostra messaggi invisibili su ogni riga di coda, non solo quelli uniti
Il distintivo e la tinta di fila erano già qui — semplicemente non erano mai popolati per la maggior parte della lista. getUnreadFor leggere solo la mappa live per-session illeggibile, che è derivato dalla fila di appartenenza dello spettatore, quindi un biglietto che non avevano unito non potrebbe mai accendersi. Su questa coda che è la maggior parte di esso, e la biglietti non firmati nessuno ha raccolto sia la maggioranza che le righe che La maggior parte ha bisogno della bandiera. Ora preferisce la mappa dal vivo, che è autorevole per le conversazioni lo spettatore è in e aggiornamenti su STOMP il momento in cui leggono o una risposta atterra, e cade torna al conto per-ticket del server per tutto il resto. I due non possono essere unito ciecamente: la mappa dal vivo porta sempre sessioni che non hanno ancora letto messaggi, quindi "leggi un secondo fa" e "mai contati" sono indistinguibili esso — da qui il piccolo insieme di conversazioni aperte durante questa visita, che mantiene una riga che il membro sta leggendo dal rimanere acceso fino al prossimo ricarica. L'elenco si rilegge anche quando il supporto in attesa si muove. Per una conversazione lo spettatore non si è unito non c'è canale live che mai correggere l'istantanea — l'argomento non letto raggiunge solo le persone nella sessione, che su una coda non è nessuno — e in attesaPaese è l'unico segnale che raggiunge ogni membro responsabile dell'org.
· MediaService
Il francobollo conta sulla lista dei biglietti e mantiene la coda in diretta
getTickets ora carica in massa un numero di messaggio non visto per riga per il chiamante, accanto le righe dei visitatori carica già in quel modo, e ottenereTicketDetail calcola la propria prima del lettore markAsRead terre — in modo che la finestra può mostrare quello che è arrivato mentre erano via piuttosto che un numero l'atto di aprirlo già azzerato. Chiude anche il divario di segnale che ha lasciato la coda stale. Un messaggio in un supporto conversazione sposta la coda per ogni membro responsabile del org, non solo le persone in quella sessione — che è esattamente quello che la sessione non ha letto argomento NON raggiunge. La trasmissione in sospeso ha già sparato quando un visitatore pubblico-chat inviato e quando un agente ha segnato lettura; un membro risponde da all'interno dell'app era il buco, quindi una conversazione di supporto in-app potrebbe guadagnare messaggi con nulla che dica lo schermo di un agente di guardare di nuovo. SupportUnseenCountSqlTest spilla la forma della query. Ogni clausola in esso è portante di carico e nessuno di loro è visibile in una costruzione di passaggio: sbagliare e la coda rende ancora, solo con i conti che sono tranquillamente falsi.
· kamo-shared-library
Contare messaggi invisibili per uno spettatore che non è nella conversazione
Unread è sempre stato derivato dalla fila MediaSessionMember dello spettatore, quindi una conversazione che non avevano aderito prodotto nessun conteggio affatto. Su una coda di supporto che è la maggior parte di esso — 27 dei 37 biglietti in diretta non sono firmati, e 26 di quelli hanno esattamente un membro della sessione, la persona che ha chiesto aiuto. Quelli sono proprio le righe che più hanno bisogno di flagging, e sono state quelle che strutturalmente non poteva portare una bandiera: nessuno li aveva uniti, quindi non c'era lettore per loro di essere illeggi. countUnseenBySessionForViewer risponde alla domanda che una coda pone. The Il JOIN sinistro sull'appartenenza è l'intero trucco: senza fila di appartenenza non c'è leggere puntatore, così ogni messaggio in entrata conta, che è la verità — lo spettatore non ha visto nessuno di loro. Una domanda per pagina, corrispondente a come le righe dei visitatori accanto sono già caricati in massa. Gli eventi di sistema sono esclusi. Un cambiamento di stato o una riassegnazione è la contabilità sulla conversazione, e contarlo illumina le righe dove nessuno ha realmente ha detto qualcosa — peggio di nessun distintivo affatto su uno schermo il cui lavoro decide che ha bisogno di una risposta. SupportTicketDTO.unseenCount è popolato sulla lista e dettagli legge e sinistra null ovunque altrimenti, perché "non visto" è un fatto su uno spettatore e un numero inventato per un chiamante che non ha chiesto circa uno sarebbe semplicemente sbagliato.
· kamo-internal
Eliminare, magneti, un menu di scelta rapida, e un orologio che chiede quale
Sei aggiunte al grafico di periodo e all'orologio nav. DELETE rimuove la pillola concentrata. Backspace troppo, perché su un Mac la chiave etichettato "delete" IS Backspace e l'offerta solo Delete significa che la scorciatoia fa non esistono su un computer portatile. Svuota entrambi i pugni dell'arco, quindi cancella un pasto lascia il lavoro sia lato di esso continuo — che è ciò che significa per pasto per non essere successo. Fase come ogni altra modifica; niente è scritto fino a salvare. Una GUIDELINE segue il puntatore su ogni riga, con il minuto che è su in un chip in cima. Il suo lavoro è quello di dire dove un clic sarebbe atterrare, e questo è il lo stesso minuto ogni giorno nel periodo. Misurare un elemento binario piuttosto che il contenitore, la cui scatola include il nome del giorno e colonne totali — utilizzando si riferisce un minuto fuori dalla loro larghezza, che il membro non può vedere RIGHT-CLICK ovunque in un giorno apre un menu che scende a un blocco di 15 minuti tempo di lavoro, pasto o pausa dove il puntatore era. Due colpi, non uno: un arco è una coppia, e un apripista solitario disegna una pillola che corre a mezzanotte. Si noti che un pasto blocco OPENS con MEAL OUT — un pasto viene registrato come orologio fuori dal lavoro e indietro dentro, e ottenere quel paio indietro invertisce il giorno. MAGNETS: un bordo trascinato in cinque minuti di un altro pugno si blocca sul suo esatto minuto, deliberatamente bypassando il gap di un minuto il morsetto mantiene. Che cosa? il divario ferma un bordo che attraversa il suo prossimo; qui l'intero intento è per i due incontrare, e un bordo che può atterrare solo un minuto corto non può mai chiudere un gap. Cinque minuti a uno zoom di quindici giorni sono un paio di pixel, quindi senza un magnete "fare questa pausa quando il prossimo inizia" è un compito che nessuno può completare con un puntatore. E quando il magnete atterra due campate del bordo tipo SAME a bordo, essi MERGE nello stesso gesto: l'orologio-out e l'orologio-in che ora condividono un minuto di stop essere veri, ed entrambi sono vuoti. Diversi tipi non si uniscono mai — un turno che termina esattamente quando un pasto inizia non è un arco, sono le due cose che in realtà E' successo. Questo aveva bisogno del tipo in GraphPunches per tie-break sull'originale minuto: due bordi coincidenti devono mantenere il loro ordine cronologico o l'accoppiamento li scambia, produce un arco di larghezza zero e un apripista, e la pillola scompare al momento i due si incontrano. THREE PILL COLORI, e distinto da più di tonalità — lavoro solido nel marchio primario, fasciato nel HRS ambra, rompere finemente spogliato in viola. Colore solo fallisce il ~8% degli uomini con una carenza verde-rosso e fallisce tutti su un proiettore; qualcuno dovrebbe essere in grado di dire un pasto non pagato da una pausa pagata in una Stampa in bianco e nero. L'NAV CLOCK chiede chi sia. "Cloccare" è tre atti diversi, e che cosa è su offerta dipende da dove si trova il membro: lavorare, possono terminare cambiare, andare a un pasto o prendere una pausa; su un pasto o una pausa, tornare è il default e terminare il turno è ancora lì perché le persone lasciano dritto da pranzo. Collapsing che in un pulsante è come qualcuno finisce il giorno in cui hanno voluto andare a pranzo — un'ora non pagata che non tornano e un Esclusione mancante sulla strada. La scheda si riconduce all'azione selezionata gli stessi tre colori che il grafico utilizza, dice quanto tempo sono stati nei loro lo stato attuale, e il punto nav ora riferisce quale dei tre stati è. Un `stato` assente dal server è letto come "il booleano è tutto quello che abbiamo", non come OFF: leggerlo come OFF offrirebbe "clock in" a qualcuno mid-shift per lunghezza della finestra di distribuzione.
· SecurityService
Relè il vuoto del pugno
Passaggio per POST /punches/{id}/void, la chiave di cancellazione dell'editor e la macchine dietro fusione di due campate adiacenti. Stessa autorità di /corretto, e per lo stesso motivo: un EMPLOYEE può annullare il loro proprio pugno, perché un membro che non può dire "quel doppio tocco non è mai successo" Non c'è modo di correggere un record a cui sono l'unico testimone. Quello che non possono fare è rendere conto — il vuoto atterra su una scheda temporale che si muove ancora attraverso catena di approvazione, e la ragione è obbligatoria su ogni percorso. L'attore è timbrato dalla SESSIONE, mai dal corpo; un attore portato dal corpo è un firma sul registro salariale di qualcun altro. Due test, tirando in direzioni opposte, perché questo servizio funziona qualsiasiRichiesta().permessoTutto() con autista laminato a mano: deve rifiutare il pugno del collega senza mai raggiungere il relè, e deve ammettere il sottoporre se stessi senza diritti affatto.
· kamo-shared-library
Un pugno può essere annullato senza essere cancellato
Quattro colonne nullable e una seconda transizione da scrittura su TimecardPunch. A void dice che il pugno non avrebbe mai dovuto esistere — un doppio rubinetto sull'orologio, un pausa abbandonata, o la cucitura tra due campate l'editor di timecard ha appena si fuse in uno. Questa non è né una correzione ("questo è successo, in un'altra tempo", che lascia una riga di sostituzione) né un'eliminazione: supersedByPunchId significa "leggi quest'altra riga invece", e qui non c'è altra riga. La fila sopravvive, annullata, con il suo autore e la sua ragione accanto a essa. Un salario record che può essere fatto per dimenticare un pugno è esattamente l'inadeguatezza che si sposta l'onere al datore di lavoro sotto Anderson v. Mt. Clemens Pottery, quindi l'unico cosa un vuoto rimuove è l'effetto del pugno sul calcolo. esecuzioneWorm() consente nulloAgli stessi termini di superatoByPunchId — null a un valore, una volta — e rifiuta un vuoto senza motivo. Non-voiding in posizione è impossibile: ri-instare un pugno significa inserire uno, che lascia la sua fila e la sua spiegazione. Non e' una cosa del genere. convenienza. La variante non filtrata è sbagliata per decidere lo stato dell'orologio: un riga sovrastata mantiene il suo istante ORIGINALE, quindi un orologio corretto all'indietro lascia la riga morta più recente da si è verificatoAtUtc, e una riga vuota rimane più recente fino a quando qualcosa di più nuovo viene colpito. O una risponde: "Sei sveglio?" da un record che non esiste più. Sequenzato per la regola di cambiamento dell'entità: condiviso-lib installato localmente, KI migrazione scritta e RUN contro 192.168.4.22:5433 con tutti e quattro gli ALTER applicato, e solo allora è questo spinto — quindi nessun servizio può costruire contro un schema non emigrato.
· InitializerService
Le colonne vuote su hr timecard punch
Specchi quattro nuove colonne nullable sull'entità TimecardPunch. Un vuoto è né un'eliminazione né una correzione: dice che il pugno non avrebbe mai dovuto esistere — un doppio rubinetto sull'orologio, una pausa abbandonata, o la cucitura tra due campate l'editor si è fuso in uno. SUPERSEDED BY PUNCH ID non può esprimerlo, perché quella colonna significa "leggere quest'altra riga invece" e non c'è altra fila. Tutti e quattro sono NULLABLE, che è ciò che rende questo sicuro su un tavolo popolato: CockroachDB rifiuta ADD COLUMN ... NON NULL in un tavolo non vuoto e rotola il l'intera dichiarazione indietro, quindi un marcatore NON NULL vuoto sarebbe registrato come un ATTENZIONE, saltato, e tranquillamente assente. Vanno al primo posto in questo corridore, davanti agli indici e lo RIVOCE già porta, per il motivo @Order(0) esiste: il momento condiviso-lib mappa una colonna, ogni corridore che carica l'entità del pugno lo emette nel suo SELECT, e un colonna-add ordinato in ritardo non funziona affatto — un lettore precedente muore su "colonna non esiste" e prende il inizializzatore giù con esso. Corri contro 192.168.4.22:5433, tutti e quattro applicati.
· kamo-internal
Mostra il pannello di bordo del mercato quando non esistono mercati CRM
/leads reso la griglia di piombo con una barra di linguetta di mercato vuota e disabilitata per un org che non aveva mai configurato un mercato CRM. Ora mostra lo stesso pannello di bordo /commercio mostra per i mercati di commercio mancanti — estratti a MarketOnboardingEmptyState in modo che i due rimangano identici — guidato dal cavo CRM mercati da Impostazioni > Client Relationship Manager, non i mercati di commercio, e puntando alla scheda CRM Markets & Fields. I passaggi e le impostazioni CTA sono mostrati solo ai membri che possono gestire CRM impostazioni; tutti gli altri ottengono la copia di check-back-later esistente.
· kamo-internal
Il grafico del periodo e una scheda temporale che si modifica come un progetto
Sostituisci la timeline di un giorno... con una sola. tutto il periodo di pagamento, e cambia quello che la modifica si sente come. Le pillole non portano messaggi. Portavano i loro tempi, e un'etichetta è 60px largo comunque lungo la cosa che descrive era — quindi una pausa di dieci minuti e una il turno di quattro ore ha disegnato la stessa pillola, e l'unica proprietà una linea temporale ha che una tavolo non (larghezza significa durata) era la proprietà che ha buttato via. Entrambi volte ora vivono in un tooltip che nomina l'inizio, la fine e l'intervallo tra loro, e segue il bordo trascinato con una lettura che aggiorna come esso Si muove. Gli angoli sono 2.5px: sufficiente per togliere la durezza meccanica, non abbastanza per arrotondare una pillola di cinque minuti in un lozenge. Le etichette dell'intestazione dell'ora ogni ora, ciascuna girava un quarto in senso antiorario così tutti i ventiquattro in forma. Quattro etichette a sei ore di distanza non possono dire a nessuno se una pillola inizia a 9 o a 10, che è l'unica domanda che l'intestazione è lì a Rispondete ora che le pillole non sono etichettate. Ogni giorno nel periodo ottiene una fila, giorni INCLUDING che non sono ancora accaduti. Un grafico che disegna solo giorni con i dati non può mostrare un pugno mancante, e il il pugno mancante è la cosa più comune sbagliata con una scheda temporale: il divario è il trovare, e un trovare ha bisogno di una fila per apparire. I giorni futuri sono incisi piuttosto "non ancora" e "nessuno registrato nulla" sono diversi fatti. Trascina lo snap al MINUTE. 5 minuti di scatto non poteva esprimere 9:02, quindi un orologio che era due minuti fuori non poteva essere corretto e ogni tempo corretto ha acquisito una morbidezza che legge come stima. L'arrotondamento è una politica del libro paga il motore si applica con il reporting di neutralità dietro di esso; un cursore non deve applicare un seconda, invisibile. Freccia chiavi nudge di un minuto, Maiusc di quindici, Alt prende una fine da solo — un puntatore non può esprimere un minuto su una pista in cui il minuto è sotto un pixel. E l'editing è ora una bozza. Dragging usato per aprire la finestra di dialogo obbligatorio-reason su ogni release: fissare un giorno con tre colpi sbagliati significa scrivere tre spiegazioni, ciascuna variante di "l'orologio era sbagliato", e una vera protezione sotto Anderson v. Mt. Clemens Pottery trasformato in una scatola persone digitate "correzione" per farla andare via. Una protezione che tutti imparano a sconfiggere protegge nessuno. Modifica ora la fase liberamente e una spiegazione è raccolta su salvare — ancora obbligatorio, ancora registrato su ogni pugno i tocchi di salvataggio, ma una volta, quando qualcuno sa cosa ha fatto. Nulla è scritto Fino ad allora. Cambiamenti di supporto: - app/lib/timecardGraph.ts detiene ogni decisione che il grafico fa — l'accoppiamento, serraggio, scatto, il cambio istantaneo DST-safe — come funzioni pure, perché un decisione su un record salariale che può essere controllato solo trascinando qualcosa con un topo è una decisione che nessuno controlla. 34 test. - fetchRange torna al giorno legge su un 404 quindi la scheda non è rotta per Chiunque lo carichi tra il SecurityService schiera e questo. - i round-trip del campo aggiuntivo a tempo pieno attraverso la parete locale. Ha reso un UTC fettare in un ingresso datetime-locale e parsed il risultato indietro come locale, quindi ogni visita al campo spostato il tempo dalla zona compensata in silenzio.
· SecurityService
Relè l'intervallo di periodo dell'editor
Passaggio per GET /members/{id}/range, il grafico del periodo è letto. da/per inoltrato solo quando presente in modo da TimecardService può risolvere la propria paga del membro periodo — inoltro "?da=&to=" si lega a stringhe vuote a valle e sconfiggere silenziosamente quel default per ogni org con un calendario di pagamento. L'identità è controllata con risolutezzaRole e nessun diritto, esattamente come /day già fa. I due requisiti tirare in direzioni opposte e un cancello a destra ottiene solo uno di loro: non deve diventare un modo per leggere le ore di un collega, e deve ammettere il soggetto stesso che tiene NOTHING, perché un membro nel Il livello RESTRICTED non ha alcun diritto e ha ancora un diritto statutario al loro un record. TimecardRangeAuthTest copre entrambe le direzioni più l'edificio di query, perché questo servizio esegue qualsiasiRichiesta().permessoTutti() con laminato a mano per-handler auth — una mappatura che dimentica il suo controllo è raggiungibile da internet pubblico attraverso La wildcard di APIService e' in avanti.
· kamo-internal
Aumentare una conversazione di supporto quando qualcuno risponde in esso
La chiusura di una finestra di supporto non lascia la conversazione, ma è stato trattato come Se lo fosse. ChatNotificationListener è tornato presto su ogni SUPPORT TICKET notifica — correttamente, dal momento che un biglietto non deve aprire una finestra di chat semplice, ma ha lasciato il supporto senza riaprire dal vivo. Chi ha chiuso il pannello, la maggior parte spesso la persona che ha chiesto aiuto una volta che avevano chiesto, semplicemente non ha mai visto risposta. Ora si apre la finestra di supporto, invece, indirizzata dal biglietto la notifica porta; il negozio di strumenti dedupes su ticketId, quindi una finestra già in su è focalizzato piuttosto che duplicato. AutoOpenUnreadSupportChats è la metà durevole, per una risposta che è arrivato mentre sono stati firmati — la spinta dal vivo è core NATS senza replay. Deliberatamente separato da AssignedTicketOpener: che si risponde "questo lavoro è ora tuo", questo risponde "qualcuno ti ha detto qualcosa", e per un richiedente il primo Non succede mai. Entrambi riconoscono attraverso lo stesso record auto-aperto chat path utilizza, quindi un secondo dispositivo non rigioca ciò che il primo ha mostrato. ChatsList ora prende ticketId direttamente dal frame di attività. Synthesizing a supporto fila senza uno prodotto una riga che non poteva essere aperta, quindi la lista aveva gettare via la cornice e rileggere l'intera pagina per ottenerla.
· MediaService
Una conversazione, molte mani — unire su aperto, nominare ogni messaggio, riaprire su una risposta
Un biglietto di supporto è lavorato da chiunque stia aiutando, non solo da chi è attualmente timbrato con. Assegnazione decide a chi il lavoro è offerto e il cui la lista si presenta in; non è mai stato destinato ad essere una serratura sulla conversazione. L'apertura di un biglietto si unisce alla sua sessione mediatica. Prima, un agente che ha aperto un il biglietto dalla coda che non è stato assegnato loro potrebbe digitare in esso — niente ha respinto il messaggio — ma non erano membri della sessione, quindi conversazione mai apparso nelle loro Chat -> Sostegno, nessuna risposta mai raggiunta loro, e il richiedente "è il mio agente qui" non li contava. Erano parlando in una stanza in cui non c'erano. Chi può unirsi è la stessa popolazione che può già vedere il biglietto in coda: le persone già nella conversazione, più ACCEPT SUPPORT TICKETS o MANAGE SUPPORT nell'org assegnato. Tutti gli altri è ora rifiutato — questo punto finale precedentemente servito qualsiasi biglietto nella piattaforma per qualsiasi membro autenticato che potrebbe indovinare un UUID, attraverso le organizzazioni, e unire cancello avrebbe ampliato che da lettura di una conversazione di uno sconosciuto a essere scritto in esso. Ogni messaggio ora porta un nome. senderName è stato letto subito Member.usernameAlias, un soprannome opzionale che non è impostato su quasi ogni membro, quindi serializzato come null e la chat ha reso messaggi senza attribuzione Tutto. Questo è peggio in un biglietto di supporto, dove più di un agente può essere rispondere e il cliente non ha altro modo di dire a chi sta parlando con loro. MemberDisplayNames — già utilizzato altrove in questo controllore — rientra nel il vero nome. La stessa lettura cruda era la cancellazione di nomi assegnati-agente, biglietto attori di storia, attori di sistema-evento, l'avviso di agente-disponibilità e tabella di presentazione. Una finestra chiusa non significa più una risposta perduta. La notifica del messaggio in arrivo porta il biglietto id, così il cliente può alzare la conversazione che sta dicendo circa, e /sessions/support/auto-open è la metà durevole per una risposta che è arrivato mentre è stato firmato fuori — la controparte del messaggio-lato assegnato-non aperto, che ha mai coperto il lavoro cambiando mani e così mai licenziato per la persona che ha chiesto aiuto. Test: ChatListNativeAliasCaseTest indica la citazione della colonna delle domande di feed alias, che niente altro può catturare — la modalità di fallimento è un nullo silenzioso, non un errore di tipo o un'eccezione. SupportAutoOpenHqlTest analizza la nuova query contro il vero modello di entità, dal momento che Spring Data convalida solo un @Query al boot e questo vive nella biblioteca condivisa, dove un typo abbatte ogni servizio.
· kamo-internal
Tavole collegabili, un Add Member funzionante e una scheda storia completa
Ogni scheda su /account/view/{id} è un percorso reale ora — /members, /history e riposo sono collegamenti che qualcuno può inviare, segnalibro, o atterrare. Old ?tab= link redirect a quella corrispondente. Ogni scheda carica i propri dati, quindi l'apertura di un account non più aspetta i dati di ogni scheda prima di mostrarne uno. Add Member ha aperto un brindisi "prossimo". Ora apre lo stesso raccoglitore il Manager usi di campo, alla ricerca di ogni membro dell'organizzazione piuttosto che solo personale — la persona che paga per un conto non è spesso sul libro paga. Persone già sul conto rimanere visibile ma insopportabile con il motivo, perché un nome che è semplicemente mancante da una ricerca legge come un picker rotto. Set-as-primary e rimuovere lavorare anche, rimuovere dietro una conferma che dice ciò che fa e non cancella. Sia la tabella che il segno del raccoglitore che il tipo di persona ogni riga è. Senza di esso membro e membro del team letto identicamente e la lista tranquillamente implica tutti su Funziona qui. Anche le righe dei membri rendono: il tipo di chiavi rivendicate endpoint non ha mai inviato, quindi ogni nome ha mostrato "-" e niente potrebbe essere rimosso - la riga id le esigenze di rimozione chiamata era uno dei campi inventati. Il pannello di storia della vista principale diventa EntityHistoryPanel, condiviso con il nuovo scheda Storia: stessi filtri, stessa paging, stessi stati vuoti. Cosa rimane con ogni chiamante è solo da dove viene il feed e che cosa i suoi ids significano.
· InitializerService
Creare ACCOUNT HISTORY
Specchi l'entità AccountHistory in kamo-shared-library. Scritto esplicitamente piuttosto che da sinistra a ddl-auto perché i valori di default sono: ID e DATE CREATED portare i default del database in modo che un semplice inserto SQL funzioni senza nominarli, che Il DDL generato da Hibernate non lo fa. Nessuna chiave straniera, di proposito — il disco supera ciò che descrive. Un account può essere fuso e un membro può lasciare l'org senza la cancellazione o il registrano quello che hanno fatto sparire con loro.
· SecurityService
Endpoint della cronologia degli account, righe dei membri reali, una directory di tutti i membri
La vista dell'account può ora dire quello che è successo a un account e chi è su di esso. - Ogni mutazione sulla superficie del conto registra la storia: creare, modificare il campo, soft delete, merge (che sposta la timeline del perdente sul sopravvissuto piuttosto che lasciarlo cadere), indirizzi, note, e il membro aggiungere / rimuovere / percorsi primari. I clienti GET/{uid}/storia e /history/filters rispecchiano gli endpoint di piombo richiesta di richiesta, perché un componente nel sito interno legge entrambi. - getCustomerMembers restituisce ciò che una riga membro deve essere reso: il display nome, avatar, titolo di lavoro, ed èTeamMember. Entrambi i tipi di persona appartengono a un conto — quello che paga spesso non è sul libro paga — e senza la bandiera i due leggono identicamente. - updateCustomerMembers accetta ids come stringhe (un unico rowid() passato MAX SAFE INTEGER arriva arrotondato altrimenti), cancella il membro primario quando il la fila che viene rimossa è loro, e non muta più l'orfano raccolta accanto al repository scrive. - La ricerca della directory richiede tutti, su ogni membro dell'org piuttosto che la lista del personale, e ogni riga dice che tipo è. Il ratchet endpoint impara a negareUnless: carica la sessione e restituisce 401 senza uno, o 403 senza il diritto, quindi un handler che si apre con esso ha fatto rigorosamente più che risolvere un chiamante. Venti voci di base sono stati elencati solo perché la scansione non poteva vedere la guardia che era già lì.
· kamo-shared-library
Un percorso di audit per account e una directory che include non-staff
ACCOUNT HISTORY registra cosa succede a un account il modo in cui LEAD HISTORY registra che cosa succede a un piombo — deliberatamente le stesse colonne, perché l'interno il sito ora rende sia attraverso un componente e una seconda forma avrebbe costretto un secondo. - AccountHistory + repository + AccountHistoryService: snapshot/diff su un campo registro, più eventi one-off per le cose che non sono modifiche di campo (un membro unione, un piombo o ordine in arrivo, una fusione). La registrazione non getta mai: un audit scrivere che fallisce non deve abbattere l'operazione che era auditing. - AccountHistoryFieldRegistry è il singolo posto in cui viene dichiarato un campo tracciato. I totali in esecuzione sono deliberatamente assenti — si muovono come effetto collaterale degli ordini, che già ottengono i propri eventi. - LeadService scrive ora la metà di un link di accesso. Attaccare un piombo a un il conto è un atto in due linee temporali, e la registrazione in un solo mezzo qualsiasi pagina che stai guardando è quello che non lo menziona. Una pista spostata tra i conti scrivono a entrambi. - Commercio Registrati di servizio ORDER PLACED / LINKED / UNLINKED / STATUS CHANG contro il conto di acquisto. Silenzioso quando un ordine non ha compratore. - No, no. la ricerca di directory solo personale allargato ad ogni membro di un org, per i raccoglitori che non scelgono un collega. Tenuto in passo con il termine TeamMemberRepo.searchDirectory, compreso il LEFT JOIN TeamMember che mantiene il titolo di lavoro e il reparto ricercabile per il personale nei risultati. attivaSolo può parlare solo per le righe che hanno uno stato di occupazione, così i membri senza uno passano invece di essere letti come "non impiegati". - Member.getPhoneExtension: getPhone() flattens to the number, lasciando l'estensione irraggiungibile dall'esterno della classe.
· kamo-internal
Confermare un orologio in / fuori prima di scrivere il pugno
L'orologio nav ha colpito direttamente dal clic. Ogni altro controllo in quella barra è idempotent o trivialmente indomabile; un pugno è un record salariale che dopo solo un amministratore HR con una ragione scritta obbligatoria può correggere — e il pulsante siede un pixel da Logout. Ora solleva TimecardPunchConfirmDialog, costruito allo standard di NewSupportRequestPopup e deliberatamente non una copia di esso: quella carta è un avviso che interrompe, quindi respira, rifiuta lo sfondo e le braccia i suoi bottoni in ritardo. Questo è stato chiesto da un clic, quindi si braccia subito e si chiude su Escape e sullo sfondo. verde per in, ambra per fuori, perché i due risultati devono essere detto a parte prima di una parola è letto. Il suo centrotavola è un orologio che ticcherà al SECOND, e la copia dice chiaramente che il tempo registrato è il momento della conferma piuttosto che il momento schermo mentre decide — il punto è che esitare è libero e nulla è scritto fino a quando non lo dici. La prima zecca è allineata al secondo confine, quindi la lettura concorda con ogni altro orologio sullo schermo del membro invece di atterraggio dovunque il clic ha fatto. Comportamento che cambiò con esso: - la chiave di idempotency viene coniata quando la finestra di dialogo OPENS e riutilizzata per una riprovazione. Per-attempt chiavi erano il bug questo previene: una richiesta che timed out può bene hanno atterrato, e riprovare sotto una chiave fresca è un secondo pugno su un record salariale. - l'azione è congelata in tempo aperto, quindi un sondaggio degli anni '60 rovescia l'orologio sotto la carta (un pugno dal telefono) non può cambiare la domanda mid-answer. - l'inclinazione ottimista è sparita. E 'esistito perché il pugno sparato dal clic; il membro ora guarda una scheda che rimane fino a quando il server risponde, quindi un stato indovinato sarebbe solo l'occasione per mostrare quello sbagliato. - un pugno fallito tiene aperta la carta e lo dice. La conversione silenziosa di un'icona è non c'è modo di conoscere un pugno che non è successo — che supera giorni dopo come un'eccezione mancante sulla scheda temporale. - l'identità di commutazione (god-mode sudo, interruttore di sessione) rilascia una conferma aperta, che altrimenti scriverebbe il pugno del membro precedente contro quello nuovo.
· kamo-internal
Filtro di stato segmentato al posto della selezione
Pending/Complete/All sono tre coetanei corti che si adattano a una linea, quindi un dropdown ha nascosto due di loro dietro un clic e ha trascorso un'etichetta e un 170px Beh, non dire niente. Una selezione chiusa non può anche rispondere "am che guardo tutto?" senza aprirlo. Segmente Filtro è condiviso: esclusivo ToggleButtonGroup, accento preso come un CSS var così ogni superficie mantiene la sua identità, e il null che esclusivo modalità mani indietro quando il segmento selezionato viene ri-cliccato viene scartato — un il filtro ha sempre un valore. Formazione assegnata lo ottiene anche, in --training-brand: i due pannelli condividono una scheda striscia, e uno di loro mantenere una discesa avrebbe letto come due prodotti diversi.
· kamo-internal
Scorrere un nav affollato invece di schiacciare le sue opzioni
Pinning più opzioni di un nav può mostrare premuto le piastrelle di forma: entrambe le strisce erano scatole di overflow grezze e le piastrelle sono bambini flessosi senza flex-shrink del proprio, quindi la barra superiore li ha distorti (e ha nascosto la sua barra di scorrimento verso l'esterno, ingoiando le opzioni senza segno che esistevano) mentre il rail ha disegnato una barra di scorrimento 12px sopra il suo gradiente. Entrambi i nav ora rendono le loro opzioni mobili attraverso NavOverflowScroller: piastrelle mantenere la loro impronta, le frecce di fine e un bordo morbido sbiadimento solo sul lato che ha ancora qualcosa da raggiungere, e la striscia può essere intestata, tenuta, ruotato, gettato con slancio, o trascinato. L'opzione della pagina corrente è ripiegato in vista all'arrivo, e la barra superiore non cromata fissa più a lungo condivide la caduta con il bambino che può assorbirlo. Dragging un'opzione e scorrere la striscia sono lo stesso gesto su un affollato nav, quindi un nav affollato mette in marcia dietro una breve presa. Che cosa? tiene il mousedown da dnd-kit piuttosto che usare il suo `delay` vincolo: il vincolo di ritardo si attiva sul timer da solo e dnd-kit sopprime il clic di qualsiasi pressa che raggiunge l'attivazione, che avrebbe ha fermato ogni clic leggermente basso su un'opzione nav da navigare.
· kamo-marketing
Indice le pagine dei dettagli della raccolta in 19 lingue in più
La sincronizzazione è terminata, così le ~2,400 pagine di dettaglio che non sono state indicizzate per avere La copia del corpo inglese ora ha traduzioni reali e può essere indicizzata. Misura in tutte le 21 localitÃ: ognuno atterra tra lo 0,0% e l'8,8% della collezione valori identici all'inglese, tranne sw e ta esattamente al 100,0% — questi due non hanno provider, quindi TranslateService ha restituito il testo sorgente e l'esecuzione ha scritto l'inglese sotto l'aspetto del successo. COLLECTION CONTENT LOCALES è quindi derivato, non elencato: ogni pubblicizzato locale meno il set no-provider. sw mantiene il suo noindex solo su pagine di dettaglio — il suo fascio di base è vero swahili, così le sue pagine statiche rimangono indicizzate — e non ottiene alcuna direttiva robot affatto, perché già canonici agli inglesi noindex originale e impilabile in cima invierebbe due segnali contraddittori circa un pezzo di contenuto.
· kamo-shared-library
Per-membro in ritardo rollup per la matrice di conformità
I rami matrici della HR Panoramica su `row.overdue > 0`, ma il legale e formazione rollup spediti con quel campo hardcoded a 0 — un ramo che potrebbe mai essere preso, in modo che quei due domini potrebbero mostrare solo ambra o verde e "piuttosto" degenerato. L'istogramma (membro, stato) non può rispondere: non trasporta dueAt. Così ognuno repository guadagna un conteggio raggruppato il cui predicato è carattere-per-character il uno in conteOverdueForOrg, più GROUP BY teamMemberId. Condividere il predicato è che cosa fa la somma delle cifre per-membro al conteggio di livello org reso sopra loro. Solo metodi di query. Nessuna modifica dell'entità, nessuna DDL, versione rimane 1.5.0. I chiamanti continuano a possedere il set aperto, e i due non sono gli stessi: passaggi legali OPEN STATES, pass di formazione RIMINEDABLE STATES. AWAITING GRADING è aperto ma non deve nulla al membro, e contarlo in ritardo riprodurrà il legale Il bug del modulo AWAITING OTHERS che ha detto ai membri in orario che erano in ritardo di settimane, Per sempre.
· kamo-internal
Costruire la scheda Panoramica come un rapporto sugli altri quattro
Console di reporting a tutta larghezza: lettura della testata, quattro pilastri della scheda, ha bisogno di assistenza, la matrice di conformità della forza lavoro e un'attività ferrovia. sola lettura — ogni azione è un legame profondo nella scheda di gestione.
· kamo-internal
Panoramica attenzione registro, matrice di conformità e rotaia di attività
utilizzareMemberNames fonti /api/security/members/names piuttosto che il breve .../org-config form: org-config non restituisce mai un elenco membro (confermato dalla lettura LegalPackageDialog.tsx e TrainingAssignDialog.tsx, che parse solo dipartimenti/lavoroTitles da esso), mentre /members/names è il endpoint già costruito per risoluzione id-to-name e già usato lo stesso modo da ipotecaAppsApi.memberNames().
· kamo-internal
Panoramica della testata e dei quattro pilastri della scheda
· kamo-internal
Panoramica dei dati gancio e primitivi visivi condivisi
useHrOverview fetches frequenza + documenti riassunti con tuttoSettled così uno il servizio morto degrada solo le sue sezioni; salta le fetches interamente per sezioni visibiliOverviewSezioni() dice che il membro non può vedere. The primitivi (CountUp, ProgressRing, StackedBar, StatePip, Figura, SectionShell) sono i blocchi di costruzione condivisi che rendono le sezioni di panoramica con, tutto il movimento sorvegliato da usoReducedMotion e EASE HOUSE.

Il tuo universo ti sta aspettando.
Aggiungi i prodotti quando sei pronto: nessuna migrazione, nessun nuovo login, nessuna richiesta di pagamento.
Gratis per sempre — nessuna carta di credito richiesta