Changelog
Ce qui est nouveau.
Chaque amélioration apportée à la plateforme, à mesure qu'elle est livrée.
· kamo-shared-library
Montrer ce que coûterait l'arrêt de l'ouverture en panne
kamo.entitlement.fail-open accorde n'importe quelle application que la matrice du plan ne mentionne jamais. C'est documenté comme une vanne temporaire pour le remblai de rétro-remplissage du catalogue - éteignez-le une fois la matrice est ensemencée - mais rien ne peut répondre à la question qui décide si l'éteindre est sûr: quels locataires détiennent quelles applications uniquement à cause de cela. Le rugissement aveugle aurait enlevé des applications d'organisations en direct, avec premier ticket d'assistance comme notification. failOpenGrants résout chaque organisation deux fois, une fois qu'il a accordé des applications non mentionnées et une fois de les nier, et rapporte la différence. Il ne lit pas le live drapeau, donc il donne la même réponse avant et après le retournement. La rerésolution passe par le plan de résolution plutôt que le plan directement, refléter la reconveillance du chemin réel à une recherche de planCode - sinon une subvention qui La surviedrait en fait comme une perte.
· kamo-internal
Montrez si quelqu'un est là, et son extension
Chaque rangée porte maintenant un point de présence et un mot à côté du nom, et son extension dans la ligne de détail - de sorte que choisir une personne ne signifie plus choisir un nom et Espérant. C'est le seul cueilleur de l'application, donc il atterrit sur le gestionnaire le terrain, les signataires de l'e-sign, l'ajout de personnes à une discussion et l'attribution de support. Battres hors ligne tout ce qui a été déclaré. disponibilité - statut ne se déplace que sur l'inscription, Signe et bascule manuelle, de sorte que quelqu'un qui ne signe jamais lise lit DISPONIBLE indéfiniment - montrer qu'en tant que point vert, il serait difficile de se tromper sur le Une chose à laquelle le point est à faire. Dessinage non connecté comme un axe de contour plutôt qu'un point rempli, donc il se lit comme une absence au lieu d'une autre couleur de statut. Un annuaire qui n'a jamais porté de présence - une source en mémoire derrière "fetcher" ne donne aucun indicateur. Absent est inconnu, et inconnu ne devrait rien dire plutôt que de prétendre que tout le monde est hors ligne.
· SecurityService
Transporter la disponibilité, la présence et l'extension sur une ligne d'annuaire
Choisir une personne est plus facile quand la rangée dit si elle est là et comment atteindre eux, donc chacun porte maintenant ce qu'ils ont déclaré en dernier, si quelque chose de Leurs sont en fait connectés, et leur extension de travail quand ils en ont une. La disponibilité et la présence sont toutes les deux ici parce que l'un ou l'autre seul induit en erreur: disponibilité - le statut ne se déplace que sur la connexion, la signature et une basculerie manuelle, donc a Un membre qui ne signe jamais de signe suit indéfiniment DISPONIBLE. La présence est lue à partir de la clé PRESENCE:-memberId- key MediaService écrit - a lire interservices vaut la peine d'être nommément. Les alternatives étaient un aller-retour HTTP pour MediaService à chaque frappe d'une recherche, ou lecture de la présence dans le navigateur, et le navigateur ne peut pas: Les fournisseurs d'outils rendent le dock de l'outil en tant que frère de la arbre contenant les fournisseurs de présence, donc un cueilleur ouvert à l'intérieur d'un ticket d'appui Une fenêtre signalerait tous les collègues hors ligne. Les deux services partagent l'hôte Redis et de la base de données. Une recherche ratée se lit comme hors ligne plutôt que d'échouer.
· BillingService
Acheter un bloc lorsqu'une org opt-in s'écourt à la sortie de la pièce
S'exécute peu de temps après le balayage d'instantanés nocturnes, parce que c'est ce qui produit le chiffre d'utilisation qu'il décide - l'augmentation par rapport au numéro d'hier achèterait un bloquer un jour de retard à chaque fois. Un bloc par course, jamais un rattrape, et il s'arrête au plafond du propriétaire. Si une organisation est bien avant ce plafond, le bon résultat est que l'écriture s'arrête Et quelqu'un le regarde, pas que la plate-forme achète tranquillement son chemin à leur les dépenses. Auto-expansé sous forme de plafond est inutilisable plutôt que illimité, parce qu'une limite absente est beaucoup plus susceptible d'être un oubli qu'une intention.
· InitializerService
Ajouter les colonnes d'auto-expansion
· kamo-shared-library
Opt-in Auto-expande avec un plafond
Deux colonnes sur l'abonnement qui détiennent les blocs: s'il faut en acheter un autre automatiquement lorsque l'organisation est à court de place, et le plus possible toujours acheter. Off par défaut à dessein - l'augmentation silencieuse de la facture de quelqu'un est pire qu'un télécharger qui échoue avec une explication, donc l'excitant est le propriétaire qui négocie un peu d'argent pour ne jamais avoir à y penser, et ce n'est que leur commerce pour faire. Un plafond est exigé plutôt que facultatif: un achat automatique sans bound is open-in to a engagement to ardemon à un processus que personne ne regarde.
· BillingService
Laissez un changement de prix de catalogue atteindre Stripe
Les prix rayés sont immuables et le mappage contient une ligne par (org, type, code, cycle) derrière une clé unique, de sorte qu'une fois qu'il existait un prix, il ne pourrait jamais changer. A L'édition des prix était une non-op permanente sur les recettes, et rien ne l'a signalé. StripePriceReconciler compare chaque prix cartographié à ce que le catalogue est maintenant dit et, quand on le demande, pointe la cartographie à un prix de Stripe frais avec le vieux un archivé - archivé plutôt que supprimé, parce que Stripe continue à facturer un le prix archivé pour les abonnements déjà sur celui-ci et le refuse pour les nouveaux, qui est exactement le comportement souhaité. Le compteur est transporté à travers, donc un compteur le prix ne peut pas revenir non mesuré et arrêter silencieusement l'utilisation de la facturation. Il ne fait délibérément pas passer les abonnés existants au nouveau prix. Silencieusement redéfinir le prix d'un client vivant parce que quelqu'un a édité une ligne de catalogue est une redevance personne n'est autorisé; s'il convient de les grand-pèrer est une décision commerciale, et Le fait qu'il n'a jamais été une décision qui manquait. Le compte qui reste encore à prendre Un prix ancien est déclaré à la place. Exposé par rapport uniquement par défaut à GET repointer derrière un POST explicite.
· InitializerService
Ajouter les colonnes qui rendent un changement de prix détectable
Ordre(0), annulable, pas de remplissage - un montant nul est écrit comme "nous ne savons pas quoi ce prix a été créé avec" et le conciliateur le traite comme inconnu plutôt que comme dérive.
· kamo-shared-library
Enregistrer ce que chaque prix Stripe a été créé avec
La cartographie stockait seulement quel prix id à utiliser, de sorte qu'un changement de prix de catalogue pourrait ne pas être remarqués. la clé unique en son genre interdisait une seconde, ce qui impliquait de porter un plan de 72 à 79 dollars. mise à jour de la page marketing, du devis in-app et du plan DTO et modification Rien sur ce que quelqu'un a été inculpé - sans rien signaler la divergence. Les centimes unitaires et les monnaies donnent au conciliateur quelque chose à comparer. A la fois nuls et gauche NULL pour les lignes existantes: null signifie "inconnu", qui est true de chaque ligne précédant la colonne, et traiter inconnue comme inadéquation serait recréer tous les prix de la succession lors de la première course.
· kamo-internal
Tapez la capacité de stockage le résumé maintenant retour
Maintient le contrat avec le client avec BillingService, qui indique maintenant ce que le plan comprend, quels blocs ont été achetés, le plafond combiné et fraction utilisée à côté des octets stockés. Le panel qui le rend a besoin d'une nouvelle copie, et le ika-translation-diction est à mi-parcours par une autre session, de sorte que l'interface utilisateur atterrit séparément plutôt que de balayer Travaux en vol dans ce repo.
· BillingService
Capacité de rapport parallèlement à l'utilisation
Le panneau de stockage signalé octets stockés contre rien du tout - pas d'allocation, Pas de pourcentage, pas d'argent - donc un propriétaire regardant 400 GB n'avait aucun moyen de dire Que ce soit à l'aise ou sur le point d'arrêter leurs téléchargements. Leur premier la connaissance d'un problème aurait été un échec du téléchargement. Le résumé porte maintenant ce que le plan comprend, quels blocs ont été achetés, plafond combiné et fraction utilisée, plus la taille et le prix du bloc, de sorte qu'un surface peut dire ce qu'un autre bloc donnerait et coûterait sans le codage dur. Tout cela provient des mêmes lignes de catalogue, le prix est facturé.
· kamo-shared-library
Donner effet à la prime de stockage
Le stockage a été mesuré sur dix-neuf domaines, tous les soirs, avec une construction en faillite. test de couverture - et rien, où que ce soit, a calculé une allocation ou refusé un octet. Le chiffre de stockage du plan était des données que personne ne lisait, et le stockage supplémentaire l'ajout n'a eu aucun effet produit: le paiement n'a rien changé et ne payant pas les coûts Rien. StorageQuotaService résout ce qu'une organisation peut détenir - le plan par utilisateur une allocation mise en commun entre ses sièges, plus les blocs qu'il a achetés - et ImageService refuse le contenu qui le dépasserait. Le chèque se trouve après le dedup recherche, donc rattacher un fichier que la plate-forme détient n'est jamais refusé pour l'espace; seulement les octets véritablement nouveaux comptent. Il vit ici plutôt que dans BillingService parce que les services qui écrivent byets ne peuvent pas appeler chaque API de facturation sur leur chemin de téléchargement, et il est injecté par plutôt que par l'intermédiaire du constructeur parce que ImageService est assemblé par main à plusieurs endroits - un argument constructeur est celui qui compile partout sauf les tests, puis échoue à la construction de l'image. Deux façons délibérées de se tromper. "GB" est lu comme un gibioctet, ce qui est ce qu'un gestionnaire de fichiers signifie quand il imprime GB et la lecture la plus généreuse. Et un une organisation sans abonnement en direct, ou un service sans quota de haricot câblé, sert exactement comme avant: cette porte existe pour maintenir un client payant à ce que ils ont acheté, pour ne pas transformer un écart de facturation en perte de données. Les lectures ne sont jamais fermées le remplissage de votre stockage vous empêche d'ajouter plus, ne vous enlève jamais ce que vous avez.
· kamo-internal
Indiquer au demandeur si son agent est effectivement là
Un fil silencieux ne distingue pas "personne n'a regardé cela" de "mon agent s'est enfui", et la personne qui attend ne peut pas dire laquelle. L'avis en vertu de la conversation dit qui, et quand la réponse est "pas maintenant", il dit ce qui se passe suivant - le message est reçu et il sera répondu lorsqu'il sera de retour - plutôt plutôt que de quitter quelqu'un pour se demander si elle a atterri. Placé sous le chat plutôt que dans l'en-tête parce qu'il répond "est n'importe qui ", quelle est la question posée en tapant, pas en scannant le les métadonnées du ticket. Fissexte, non lu à partir de la carte de présence du demandeur: la présence est diffusion par organisation, donc un membre client-org aidé par le fournisseur org considérerait leur agent comme hors ligne en permanence. Un faux "ils sont Il est pire que de ne pas s'en tenir compte. Pour la même raison, un sondage raté conserve le dernier la réponse connue au lieu de retourner l'avis. Le scrutin à 30 ans et seulement tant que la languette est visible. La disponibilité est un confort signal, pas un argument correcteur, et un onglet d'arrière-plan n'est personne ne regarde.
· MediaService
La réaffectation parvient au nouvel agent, et le demandeur est informé de qui est là
La réaffectation n'a atteint personne. AgencesOuvrirA enregistré qu'un client a mis la conversation à l'écran, et c'est ce qui soulève un ticket à la connexion - mais il Je tenais toujours le timbre de l'agent PREVIOUS, donc le nouveau a obtenu le push en direct seulement si Ils se sont trouvés être en ligne cette seconde, et rien du tout s'ils étaient La cession s'ensuivi est désormais effacée chaque fois que le cessionnaire change effectivement, qui rend le transfert durable pour quiconque le possède maintenant. GET - indique à la personne qui a demandé de l'aide si la personne qui les aide est accessible. Possibilité faite à nouveau côté du serveur: présence est diffusé par organisation, de sorte qu'un demandeur client-org ne peut pas voir un agent dans l'organisme de prestation de services et le statut se lirait "hors ligne" pour chaque personne conversation entre les autres. Porté aux personnes déjà au cours de la conversation - a ticket id est un UUID, pas un secret, et "est cette personne à son bureau" ne devrait pas répondre à une devinette.
· kamo-internal
Affecter une demande de discussion et attribuer à partir de la liste des tickets
Deux lacunes, même cause - l'affectation a été traitée comme quelque chose qui arrive à un ticket plutôt qu'à un travail. Le bouton d'attribution était caché pour les pré-tickets purs, donc une demande de chat en direct, le cas où l'accès à la bonne personne est le plus important, n'avait aucun moyen d'être remises. Cette exclusion date du moment où une demande de discussion n'était pas encore "a le ticket réel" et l'affectation n'ont eu lieu que lors de la conversion. Il est assignable maintenant, et le statut survit, de sorte qu'il peut encore être converti par la suite. Et l'assignement nécessitait d'ouvrir la conversation d'abord, ce qui est la mauvaise forme pour trier une file d'attente - la personne qui travaille la file d'attente n'est souvent pas la personne qui devrait répondre. La cellule Assigned To sur la liste des tickets est maintenant le contrôle: Les émissions en vol stationnaire assignent ou réassignent, le clic ouvre le même picker le ticket les utilisations de fenêtres. Le clic de rangée ouvre toujours la conversation. La réponse patche sa propre rangée plutôt que de recharger, de sorte que l'attribution n'est pas déplacer la place de l'opérateur dans une file d'attente de page filtrée, triée.
· kamo-internal
Moteur de citation pour le préparateur de plan, en correspondance avec ce que Stripe va facturer
L'arithmétique derrière un total en direct, maintenu à l'écart de n'importe quel composant de sorte que le les nombres peuvent être testés directement. Il se reflète. exactement: échelle d'appoint par siège avec effectif, facture forfaitaire par unité achetée Cependant, de nombreuses personnes sont sur le plan, et les personnes mesurées sont nommées plutôt que deviné. Cette correspondance est le point. Les deux place la plate-forme citant précédemment un prix n'est pas d'accord avec la redevance et l'assistant d'ordre dans l'application a ajouté chaque add-on une fois indépendamment des sièges, et la page publique facturée une piscine de stockage à l'échelle de l'organisation par utilisateur.
· BillingService
Rendre un premier plan achat et retourner des prix réels
Deux choses ont empêché une organisation d'acheter un premier abonnement. Les listes de plans n'étaient accessibles que par le biais de la sous-enchère.marketId sur l'abonnement. D'une durée déjà détenue, de sorte qu'une barre avec aucune personne ne pouvait voir de plan nulle part dans le produit. GET/api/billing/markets/for-org résout le marché de l'appelant la racine d'accès - la même org dont le plan libre fournit déjà leur niveau de référence, Ainsi, les plans et les droits ne peuvent pas provenir de différents catalogues. Et le plan DTO lire - ce qui n'est rien toujours populés: ils sont NULL pour chaque plan KamoCRM, parce que le semoir écrit C'est donc à une surface d'achat indiquée. aucun prix du tout, et le compte a enregistré une redevance nulle. Il lit maintenant le prix lignes, avec les colonnes de plan comme solution de secours pour les catalogues qui les portent. Dévoit également la taille de l'unité d'insertion afin qu'une surface d'achat puisse dire "par 10 GB" sans le codage dur.
· kamo-internal
Porte payée des actions en siège titulaire, au point d'étranglement
Presque toutes les actions gateables dans le produit et composer une extension, envoyer un courriel à un membre, ouvrir l'assistant d'IA, jouer un enregistrement, lancer une conversation sociale et est nue - et le registre des outils n'avait aucune porte d'entrée. Porter ces sites d'appels un à la fois ne pourrait jamais être complet: un répartiteur ajoutés plus tard, ou simplement manqués, des itinéraires tout droit autour. Le chèque vit maintenant dans Outil unique de ToolWindowsContext: un manipulateur ouvert, de sorte que chaque appelant est couvert. Les actions qui ne passent jamais par une fenêtre d'outil sont fermées là où elles vivent: les attaches (y compris la glisser-déposer et la pâte, qui sont la même capacité en un autre itinéraire), une discussion en groupe, et les points d'entrée de la réunion. Cela permet également de régler trois incohérences relevées par l'audit. Deux des trois "inviter à se réunir" points d'entrée vérifiés organisation.isMeet et le troisième a fait non; deux des trois points d'entrée par courrier électronique ont vérifié ACCESS-MAIL et le troisième l'a fait. et la prolongation d'un membre à partir de la liste n'a été effectuée avec aucun contrôle VOIP genre. Tous sont maintenant d'accord. Capacités par défaut à verrouillées - pendant que la première charge est en vol, si l'appel échoue, et si un backend plus âgé omet le champ. Vois-vieillir "autorisé" aurait la main les caractéristiques payées, c'est-à-dire l'erreur que le chemin de droit déjà fait une fois avec sa défaillance par défaut.
· SecurityService
Résoudre ce qu'un membre peut faire, et pas seulement ce que son plan comprend
Les subventions en fonction décrivent un plan. Ils n'ont jamais décrit ce membre, parce que le b) Un membre sans licence de la console est un membre sans licence ont vu chaque application à laquelle l'organisation avait droit, et le niveau des membres L'octroi de licences qui existaient déjà dans la base de données n'a eu aucun effet sur quoi que ce soit. MemberCapabilities est cette réponse manquante, résolue une fois sur le serveur. Un membre sans siège garde un plancher délibéré: une conversation textuelle avec ses coéquipiers, soutenir, leur propre profil, leur propre facturation - suffisamment pour atteindre un être humain et Achetez un siège au dos. La traduction en désaction est aussi continue, parce que lire quoi vous avez été envoyé n'est pas dépenser. Tout ce qui coûte de l'argent à courir est éteint. Un procès en og actif donne droit à toute personne dans l'organisation plutôt que seulement au siège porteur. Un groupe d'essai est ensemencé avec le siège simple du plan libre, de sorte que le plan libre. Un essai sur les licences aurait permis au propriétaire seul d'évaluer le produit alors que l'équipe qu'ils ont invité à l'évaluer n'a rien vu. La connexion n'est pas affectée: l'authentification n'a jamais consulté l'état de facturation et toujours non. Perte d'un siège est fonction, n'y a jamais accès.
· BillingService
Bille additionnel par unité par unité d'unités achetées, et non par nombre d'unités
File la nouvelle quantité d'add-on par la création et la mise à jour: un add-on FLAT maintenant prend sa quantité Stripe à partir de ce que l'abonnement a réellement acheté, donc quatre 10 GB bloquent la facture en tant que blocs plutôt qu'une fois par siège. Les quantités sont filtrées contre les codes additionnels résolus sur la voie en, donc a corps arraché ou hostile ne peut pas garer une quantité contre un code auquel il n'avait pas droit à acheter.
· InitializerService
Stockage supplémentaire de remplacement à 2,50 dollars par 10 Go, dans l'ensemble du GB
L'ajout décrivait un pool partagé à l'échelle de l'organisation et a ensuite été évalué USAGE-BASÉ à 4 avec "25 GB" indiqué uniquement en prose. Rien n'a jamais présenté de événement de mécanicien de stockage, donc il n'a rien facturé; et s'il avait été attaché, Stripe rejette une quantité sur un prix dosé, de sorte que la création d'abonnement aurait Jet. Entre-temps, la page publique la rendait comme « 4 euros/utilisateur/mois ». C'est maintenant FLAT avec l'unité - taille 10 à 2,50 dollars - obtient le propriétaire, le prix Mais il en va de même pour de nombreuses personnes. AddOnQuantityMigration ajoute les deux colonnes annulables à l'ordre(0), avant qu'un coureur puisse charger une entité qui cartographie eux.
· kamo-shared-library
Donner des add-ons d'une taille unitaire et d'une quantité achetable
Une unité d'appoint n'a jamais existé que sous forme de prose dans sa description ("Chaque unité ajoute 25 GB") et en tant que constante codée en dur sur le site de commercialisation, donc rien ne pourrait facture contre elle. SubscriptionAddOn gagne l'unité de taille et gains d'abonnement au compte additionon-quantités - combien d'unités de chaque add-on l'abonnement acheté. les codes actifs restent la source de la vérité, donc rien que seulement Demande que "est cette application accordée" doit savoir que la carte de quantité existe. Corrige également la citation de l'assistant d'ordre, qui a ajouté chaque add-on une fois sans sièges facteur même si la plupart sont PER-SEAT: une commande de 25 sièges avec quatre modules a été cité le montant de 31 dollars contre le montant de 775 dollars Stripe. La citation suit maintenant la même chose. le modèle de tarification fonctionne, et omet les add-ons comportés plutôt que de deviner.
· kamo-internal
Choisissez l'agent pour réaffecter un billet à, au lieu de taper son id
Ajoutez et réassignez une zone de texte que vous avez tapé un id brut de membre dans lequel signifiait déjà connaître l'id de la personne que vous vouliez, pour une action dont le tout Le but est de trouver le bon. Il utilise maintenant MemberPickerDialog, le même présageur derrière le champ Manager sur un Le profil du membre et le seul cueilleur de personnes dans cette application. Sa recherche d'annuaire s'effectue serveur et correspondances sur le nom, l'alias, le courrier électronique du travail et du compte, le titre du poste et département, donc "ana prêt" trouve l'Ana dans le prêt; chaque ligne porte suffisamment pour Dis à deux personnes du même nom. Le cessionnaire actuel est encadré au-dessus de la Il n'est donc pas perdu à mi-recherche. Les membres ids dépassent la gamme entière exacte de JavaScript, de sorte que l'id reste une corde à partir de le répertoire du fil.
· MediaService
Points d'arrêt pour le transfert auto-ouvert de l'inte
GET /sessions/chat/auto-open retourne les conversations de ce membre client devrait recueillir des enregistrements qu'il a pris ou non. La durabilité contrepartie du transfert de soutien, et pour la même raison: la décision appartient à la plate-forme, et non à l'onglet un navigateur sessionStorage. L'écriture est portée à la ligne d'adhésion de l'appelant, de sorte que le client d'un membre ne peut jamais retirer un autre, et timide le temps du serveur plutôt qu'un client fourni valeur - la comparaison est par rapport à des horodatages de message écrits par cette même horloge, et un navigateur biaisé supprimerait sinon une conversation pour toujours ou Re le relevez sans fin. ChatAutoOpenHqlTest analyse l'interrogation par rapport au modèle d'entité réel et épingle le trois prédicats pour lesquels il existe. Les données de printemps ne valident que la situation dans le contexte boot, donc une frappe ferait autrement surface comme une startup en panne ou une 500 sur un chemin chaque membre frappe à la connexion; le test lit la requête de l'annotation par la réflexion de sorte qu'elle ne peut pas passer contre une copie évincative.
· kamo-shared-library
Enregistre durable d'une conversation que le client a soulevée par lui-même
MediaSessionMembre gagne AUTTO-OPENED-AT, à côté de msgIdLastViewed parce que c'est le le même grain - un fait sur la relation d'un membre à une session. Celui-là répond "comment ont-ils lu" ; celui-ci répond "qu'ils ont déjà été montrés Cela, non demandé ». Il remplace un drapeau de session par onglets, qui n'était pas un enregistrement de quoi que ce soit: a un deuxième appareil a rouvert chaque conversation que la première avait déjà fait sauter, et L'enlèvement du stockage a entièrement perdu le fait. findChatSessionsToAutoOpen rend les sessions qui tiennent quelque chose que le membre a ne pas lire où quelque chose est arrivé depuis la dernière augmentation - donc une conversation volontairement non lu, il s'arrête de rouvrir à chaque chargement mais revient quand Un message véritablement nouveau atterrit. Séances de discussion uniquement; les tickets d'assistance ont leur propre le transfert. Les messages du membre sont exclus, et un msgId en suspension Le pointeur compte comme non lu plutôt que comme rattrapé, parce qu'une sous-requête scalaire sur une ligne disparue donne NULL et serait lue comme lue en permanence.
· kamo-internal
Réuniser le panel sur les préférences
Quatre décrochages et un interrupteur sur un panneau lavé de haut en bas dans le locataire la couleur à pleine force, de sorte que chaque contrôle a combattu son propre fond. Chacune Sélectionner trois ou quatre options cachées derrière un clic et répéter les mêmes douze lignes de style frontalier. Le panneau est maintenant une surface, et la couleur appartient à tout ce qui est choisi. - Chaque option est à l'écran. Il s'agit de quatre réglages avec trois à cinq valeurs courtes chacune; les montrer est plus rapide à utiliser que l'ouverture d'un menu, et Un composant Choix remplace quatre blocs de style dupliqué. - Reminder le temps et la méthode de partage d'une carte, car ni l'un ni l'autre ne signifie quoi que ce soit sans l'autre. Les offsets de rappel sont mono, correspondant au reste du le traitement du temps du calendrier. - Le calendrier par défaut indique sa couleur, de sorte qu'il est reconnu de la même manière qu'il est dans la barre latérale et le dialogue de l'événement plutôt que par nom seul. - L'en-tête reste en place pendant que le corps défile - avec quatre sections le titre sinon s'éloigne. Ces paramètres s'économisent sur le changement sans aucun bouton pour appuyer, et précédemment dit donc avec une pinnière nue. Il y a maintenant un "Sauvegardé", explicite, qui s'estompe après quelques secondes parce que c'est un reçu pour le changement qui vient d'être fait, pas un l'état permanent du panel. Les sections s'accroissent et les ressorts de confirmation; les deux sont supprimés sous préfère-réduction-mouvement. Chaque corde est une clé existante. Vérifié avec une construction de production dans un arbre de travail détaché.
· kamo-internal
Remplacer les fileuses de boîte aux lettres par un squelette qui assemble
Chaque état de chargement sur /messages était un chargeur2 sur une vitre par ailleurs vide. boot, la liste de messages, l'arborescence de dossiers, le corps du message et la page suivante tous s'étend sur place et n'a rien dit au lecteur sur ce qui allait arriver. Ils dessinent maintenant ce qui est le chargement. Chaque espace réservé copie la géométrie de le composant qu'il représente, jusqu'à l'avatar 40px, l'identifiant de dossier et la ligne de message 104/120px mesurée sur le balisage réel, de sorte que la mise en page n'est pas se déplacer lorsque les données atterrissent. Une idée porte le mouvement: une bande de croix de lumière chaque espace réservé de gauche à droite et commence un battement plus tard sur chaque ligne en dessous, donc le point culminant descend la pile comme une seule diagonale plutôt que chaque ligne clignotant sur sa propre horloge. Les rangées s'alignent dans le même ordre. La lumière est mélangée à partir de --accent-comms, l'accent de la famille du module des messages, Il se lit donc comme ce produit plutôt qu'un gloss générique. préfère-réduction-mouvement aplatir tout le tout pour les barres tranquilles. Pas de nouvelles clés de messagerie: les espaces réservés s'annoncent pour écranr les lecteurs à travers les trois cordes de chargement qui étaient déjà là.
· kamo-internal
Ajouter et supprimer les invités du dialogue de l'événement
La dernière chose que le dialogue ne pouvait pas faire. Il pouvait montrer qui était déjà allumé un événement, mais il n'y avait aucun moyen d'y mettre quelqu'un - le seul contrôle d'invité était "mise à jour et notification", qui envoie un nouveau courrier électronique aux personnes qui étaient déjà invitées. Les invités sont ajoutés par courriel, listés avec leur RSVP comme un point de couleur intitulé avec l'état, et enlevé sur le vol stationnaire. Enter ajoute un invité plutôt que soumettre le dialogue, parce que vous êtes de la liste intermédiaire plutôt que finie. Le l'organisateur est marqué et ne peut pas être pris pour un invité ordinaire. La validation du courrier électronique est délibérément permissive: le test réel est de savoir si le courrier à lui rebondit, et un modèle plus strict rejette les adresses qui sont parfaitement valable. Il attrape une faute de frappe évidente, rien de plus. L'état vide invite plutôt que des excuses. Chaînes expédiées avant cela en dictionnaire de transformation en kômo-dictionnaire jette sur une clé manquante des clones CI en.json et kamo-internal's CI qui repo au moment de la construction, donc l'atterrissage d'abord aurait livré une page de calendrier qui crashs. Vérifié avec une production construite dans un arbre de travail détaché; une autre session détient l'écluse dans l'arbre.
· EmailService
Gérer la liste des invités d'un événement
Le dialogue pourrait montrer qui était déjà sur un événement mais ne jamais ajouter ou supprimer n'importe qui; la seule opération d'invité était de re-notifier les gens qui étaient en quelque sorte déjà là. Les événements acceptent maintenant une liste d'invités, remplacé en gros sur épargne. Remplacer plutôt que la fusion, parce que le dialogue possède la liste - un invité enlevé là-bas doit Disparurer ici. Une liste nulle signifie "non fourni" et laisse les invités seul, ce qui est ce qu'un client n'éditant qu'un titre envoie. Deux choses qu'un client n'a pas à affirmer, donc pas plus est lu sur le demande: le RSVP, qui est l'invité à donner, et le drapeau de l'organisateur, quels procès-verbaux qui ont convoqué la séance. Les deux survivent à un éditeur et à la recréation les lignes au lieu de les mettre à jour réinitialiseraient chaque réponse et enlèveraient la seule une personne autorisée à annuler l'événement. Les invités reviennent par lots. Un jour de vision rend une centaine d'événements, et demander par événement est une requête par ligne. La suppression d'un événement vient d'abord d'abord d'enlever ses invités. Ils sont équipés d'un non-nulle la clé étrangère à elle, de sorte que la suppression soit de les orphelinr, soit de l'échouer une fois la base de données a imposé la référence.
· kamo-shared-library
Consultation d'invité par lots, et suppression par événement
Le dialogue de l'événement indique avec qui est un événement, et le point de terminaison d'état d'évaluation de la liste retourne un mois d'événements à la fois. Demander aux invités par événement est une requête par ligne; findByEventIdIn demande une fois. deleteByEventId est la première moitié du remplacement d'une liste d'invités en gros, et de l'enlèvement d'un événement sans orphelines qui transportent un étranger non nul la clé à en revenir.

Votre univers vous attend.
Commencez gratuitement en quelques minutes. Ajoutez des produits quand vous êtes prêt. Pas de migration, pas de nouvelles connexions.
Gratuit pour toujours — aucune carte de crédit requise