Merge pull request #9719 from alexrybch/gcalendar
Google calendar widget for Homepage
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-gcalendar': minor
|
||||
---
|
||||
|
||||
Added Google calendar widget
|
||||
+102
-102
@@ -2,105 +2,105 @@
|
||||
|
||||
_If you're using Backstage in your organization, please try to add your company name to this list. This really helps the project to gain momentum and credibility. It's a small contribution back to the project with a big impact._
|
||||
|
||||
| Organization | Contact | Description of Use |
|
||||
| --------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| [Spotify](https://www.spotify.com) | [@leemills83](https://github.com/leemills83) | Main interface towards all of Spotify's infrastructure and technical documentation. |
|
||||
| [bol.com](https://www.bol.com) | [@sagacity](https://github.com/sagacity) | Initial work being done to unify platform tooling. |
|
||||
| [DFDS](https://www.dfds.com) | [@carlsendk](https://github.com/carlsendk) | V2 self-service platform. |
|
||||
| [Roadie](https://roadie.io) | [@dtuite](https://github.com/dtuite) | Hosted, managed Backstage with easy set-up |
|
||||
| [Roku](https://www.roku.com) | [@timurista](https://github.com/timurista) | Initial work on Cloud engineering service platform. |
|
||||
| [SDA SE](https://sda.se) | [@dschwank](https://github.com/dschwank), [@iammnils](https://github.com/iammnils) | Central place for developing and sharing services in our insurance ecosystem. |
|
||||
| [H-E-B](https://www.heb.com) | [@german-j-rodriguez](https://github.com/german-j-rodriguez) | Initial work on Engineering Portal service platform. |
|
||||
| [American Airlines](https://www.aa.com) | [@paulpach](https://github.com/paulpach) | Central place for developers to develop and maintain applications |
|
||||
| [Kiwi.com](https://kiwi.com) | [@aexvir](https://github.com/aexvir) | Replacing the frontend of [The Zoo](https://github.com/kiwicom/the-zoo), their service registry. |
|
||||
| [Voi](https://www.voiscooters.com/) | [@K-Phoen](https://github.com/K-Phoen) | Developer portal, main gateway to our infrastructure, documentation and internal tooling. |
|
||||
| [Talkdesk](https://www.talkdesk.com) | [@jaime-talkdesk](https://github.com/jaime-talkdesk) | Initial work for Engineering Portal and Self Provisioning to R&D |
|
||||
| [Wealthsimple](https://www.wealthsimple.com) | [@andrewthauer](https://github.com/andrewthauer) | Developer portal, service catalog, documentation and tooling |
|
||||
| [Grab](https://www.grab.com) | [@althafh](https://github.com/althafh) | Initial work as a unified interface for all of Grab's internal tooling |
|
||||
| [Telenor Sweden](https://www.telenor.se) | [@O5ten](https://github.com/O5ten) | Building a developer portal for scaffolding projects towards our unified build environment and microservice stacks |
|
||||
| [Fiverr](https://www.fiverr.com) | [@nirga](https://github.com/nirga) | Unifying separate tools that developers are using today (i.e. monitoring, dead letter queues management, etc.) into a single platform. |
|
||||
| [Zalando SE](https://www.zalando.de) | [@leviferreira](https://github.com/leviferreira) | Building V2 of the Internal Development Portal. |
|
||||
| [LegalZoom](https://legalzoom.com) | [@backjo](https://github.com/backjo) | Developer portal - hub for all engineering projects and metadata. |
|
||||
| [Expedia Group](https://www.expediagroup.com) | [Mike Turner](mailto:miturner@expediagroup.com), [Sneha Kumar](mailto:snkumar@expediagroup.com), [@guillermomanzo](https://github.com/guillermomanzo), [Erik Lindgren](https://github.com/lindgren) | EG Common Developer Toolkit |
|
||||
| [Paddle.com](https://paddle.com) | [Ioannis Georgoulas](https://github.com/geototti21) | Developer portal (Tech Docs, Service Catalog, Internal Tooling), we use vanilla Backstage FE and custom BE implementation in Go |
|
||||
| [Acast.com](https://acast.com) | [Olle Lundberg](https://github.com/lndbrg) | Developer portal with tech docs, service catalog and a bunch of other internal tooling |
|
||||
| [Lunar](https://lunar.app) | [Jacob Valdemar](https://github.com/JacobValdemar) | Internal developer portal for service overview and insights, API documentation, technical guides, onboarding guides and RFC's. |
|
||||
| [Trendyol](https://trendyol.com) | [Gamze Senturk](https://github.com/gmzsenturk), [Mert Can Bilgic](https://github.com/mertcb) | The Developer Portal has been called `Pandora`. Provides an overview of Trendyol tech ecosystem. TechDocs, Catalog, Custom Plugins and Theme. |
|
||||
| [Peloton](https://www.onepeloton.com/) | [Jim Haughwout](https://github.com/JimHaughwout) | Creating our first developer portal and tech-docs. Exploring Service Catalog, Tech Insights and Cost Insights as well. |
|
||||
| [TELUS](https://telus.com) | [Seb Barre](https://github.com/sbarre) | The Go-to place to find answers about development and delivery at TELUS. |
|
||||
| [Brex](https://www.brex.com/) | [Vamsi Chitters](https://github.com/vamsikc) | A centralized UI to understand how a service fits in the whole Brex architecture and manage a team’s engineering dependencies. |
|
||||
| [Oriflame](https://www.oriflame.com/) | [Oriflame](https://github.com/oriflame) | Internal developer portal for services, single page apps and packages overview, API documentation, technical guides, tech-radar and more. |
|
||||
| [Booz Allen Hamilton](https://www.boozallen.com/) | [Jason Miller](https://github.com/JasonMiller-BAH) | Developer portal for a full-stack software development ecosystem that accelerates consistent and repeatable Modern Software Development practices for internal innovation and investments. |
|
||||
| [Netflix](https://www.netflix.com/) | [bleathem](https://github.com/bleathem) | Our Backstage implementation will be the front door to a unified experience connecting our internal platform products across important workflows with integrated knowledge and support. |
|
||||
| [b.well](https://www.icanbwell.com/) | [Jacob Rosales](https://github.com/jrosales) | Foundation for our engineering portal and cloud insights. |
|
||||
| [PagerDuty](https://www.pagerduty.com/) | [Mark Shaw](https://github.com/markshawtoronto) | Developer portal, initially focused on software templates and tech-docs. |
|
||||
| [MoonShiner](https://moonshiner.at) | [Fabian Hippmann](https://github.com/FabianHippmann) | Developer portal - helps us keep track of our customer projects, onboard new developers & improve our development process 🌕🚀🧑🚀 |
|
||||
| [FundApps](https://www.fundapps.co/) | [Elliot Greenwood](https://github.com/egnwd) | Developer Portal - A place for us to keep track of our projects and documentation for all services and processes |
|
||||
| [DAZN](https://dazn.com/) | [Lou Bichard](https://twitter.com/loujaybee), [Marco Crivellaro](https://github.com/crivetechie), [Alex Hollerith](mailto:alex.hollerith@dazn.com) | Ingesting all of DAZN's repos for the catalog, migrating our internal platform apps (pull request boards, release information, inner source marketplace etc) to Backstage plugins (where applicable). |
|
||||
| [HelloFresh](https://www.hellofresh.de/) | [@iammuho](https://github.com/iammuho), [@ElenaForester](https://github.com/ElenaForester), [@diegomarangoni](https://github.com/diegomarangoni) | Our developer portal at HelloFresh - Spread across an organisation of 500+ engineers globally. |
|
||||
| [FactSet](https://www.factset.com/) | [@kuangp](https://github.com/kuangp) | Developer portal to provide discoverability to all internal components, APIs, documentation, and scaffold templates with integrations to our internal infrastructure tools. |
|
||||
| [Workrise](https://www.workrise.com/) | [Michael Rode](https://github.com/michaelrode) | Developer portal, main gateway to our infrastructure, documentation and internal tooling. |
|
||||
| [RedVentures](https://www.redventures.com/) | [Chris Diaz](https://github.com/codingdiaz) | Developer portal that brings everything an engineer needs to provide value into a single pane of glass. |
|
||||
| [MavTek](https://www.mavtek.com/) | [@fgascon](https://github.com/fgascon) | Developer portal focused on standardizing practices, centralizing documentation and streamlining developer practices. |
|
||||
| [QuintoAndar](https://www.quintoandar.com.br/) | [@quintoandar](https://github.com/quintoandar) | Developer portal, services catalog and centralization of service metrics. |
|
||||
| [empathy.co](https://empathy.co/) | [@guillermotti](https://github.com/guillermotti) | Developer portal for tech docs, service catalog, plugin discovery and much more. |
|
||||
| [creditas.com](https://creditas.com/) | [@aureliosaraiva](https://github.com/aureliosaraiva) [@Creditas](https://github.com/creditas) | Centralization of all services, standards, documentation, etc. We started the deployment process. |
|
||||
| [Prisjakt](https://www.prisjakt.nu) / [PriceSpy](https://pricespy.co.uk) | [@kennylindahl](https://github.com/kennylindahl) | Internal developer portal - Documentation, scaffolding, software catalog, TechRadar, Gitlab org data integration |
|
||||
| [Powerspike](https://powerspike.tv/) | [@trelore](https://github.com/trelore) | Developer portal for documentation of core libraries and repositories. |
|
||||
| [2U](https://2u.com) | [Andrew Thal](https://github.com/athal7) | Development team home-base, promoting service discoverability, resource dependencies, and tech radar |
|
||||
| [Taxfix](https://taxfix.de/) | [Sami Ur Rehman](https://github.com/samiurrehman92) | Developer's portal with software catalog at it's core. Hosts API Specs, Tech Docs, Tech Radar and some custom plugins. |
|
||||
| [Busuu](https://busuu.com/) | [Adam Tester](https://github.com/adamtester) | Developer portal with service catalog, API docs, Event docs, service templating, and cost insights. |
|
||||
| [Loadsmart](https://loadsmart.com/) | [Loadsmart](https://github.com/loadsmart) | Improve services visibility and operations for service owners and developers. |
|
||||
| [Monzo](https://monzo.com/) | [@WillSewell](https://github.com/WillSewell), [@joechrisellis](https://github.com/joechrisellis) | Developer portal showing metadata and docs for over 2000 microservices. We have built a number of plugins such as a UI for our system to measure [software excellence](https://monzo.com/blog/2021/09/15/how-we-measure-software-excellence), and a UI to show deployment and config change events. |
|
||||
| [Vaimo](https://www.vaimo.com) | [@vaimo-magnus](https://github.com/vaimo-magnus) | Developer Portal for our developers at Vaimo, currently docs and self-service towards our internal PaaS based on k8s. Plans to extend the catalog into Projects, Environments etc |
|
||||
| [Wayfair](https://www.wayfair.com) | [@fransan](https://github.com/fransan), [@errskipower](https://github.com/errskipower), [@hrrs](https://github.com/hrrs) | Developer portal for service catalog, technical documentation, and APIs. |
|
||||
| [CircleHD](https://www.circlehd.com) | [@circlehddev](https://github.com/circlehddev) | Developer Portal for internal dev team across the globe |
|
||||
| [CastDesk](https://castdesk.com) | [@circlehddev](https://github.com/circlehddev) | Developer Portal for internal dev team across the globe |
|
||||
| [Santagostino](https://santagostino.it) | [@santagostino](https://github.com/santagostino) | Developer portal, gateway to our infrastructure, documentation, service catalog and internal tooling. |
|
||||
| [Peak](https://peak.ai) | [Luke Beamish](https://github.com/lukebeamish-peak) | Developer portal for all internal engineers to access documentation and tooling. |
|
||||
| [Gelato](https://gelato.com/) | [Dmitry Makarenko](https://github.com/dmitry-makarenko-gelato) | Developer portal: documentation, service templates, org structure, service catalog, plugins for integration with internal and third-party systems🚀. |
|
||||
| [GoCardless](https://gocardless.com/) | [James Turley](https://github.com/tragiclifestories) | Developer portal: documentation, service templates, org structure, service catalog, plugins for integration with internal systems. |
|
||||
| [Box](https://www.box.com) | [@kielosz](https://github.com/kielosz), [@jluk-box](https://github.com/jluk-box), [@ptychu](https://github.com/ptychu), [@alexrybch](https://github.com/alexrybch), [@szubster](https://github.com/szubster) | Developer portal for service catalog, integration with internal systems, new service onboarding. |
|
||||
| [Bazaarvoice](https://www.bazaarvoice.com) | [@niallmccullagh](https://github.com/niallmccullagh) | Developer portal for service catalog and scaffolds, publishing Github docs and API documentation, visualising our internal tech radar and our product engineering org structure. |
|
||||
| [Krateo PlatformOps](https://www.krateo.io) | [@projectkerberus](https://github.com/projectkerberus) | A multi-cloud control plane to create, manage and deploy any kind of resource easily and centrally via a Developer Portal that centralizes via a self-service catalog the templating and ownership of services, the available documentation, the overview of the components that compose an entire domain and all the data of the service lifecycle. |
|
||||
| [Adevinta](https://www.adevinta.com) | [Ray Sinnema](https://github.com/RemonSinnema) | Showcase shared services to our internal customers. |
|
||||
| [Splunk](https://www.splunk.com) | [@tonytamsf](https://github.com/tonytamsf) | Developer portal as a centralized place to find people, services, documentation, escalation policies and give bravos. This portal is also being used as a centralized search engine for engineering specific documentation. |
|
||||
| [SoundCloud](https://www.soundcloud.com) | [Julio Zynger](https://github.com/julioz) | Developer portal as a [humane registry](https://martinfowler.com/bliki/HumaneRegistry.html) for the organization: catalog of people, services, documentation, feature toggles, escalation policies, etc. |
|
||||
| [Volvofinans Bank](https://www.volvofinans.se) | [Johan Hammar](https://github.com/johanhammar) | Developer portal enabling engineers to manage and explore software and documentation. |
|
||||
| [Palo Alto Networks](https://www.paloaltonetworks.com) | [Jeremy Guarini](https://github.com/jeremyguarini), [Brian Lomeland](https://github.com/bbbmmmlll), [Palo Alto Networks](https://github.com/PaloAltoNetworks) | Developer portal, service catalog, documentation and tooling |
|
||||
| [Signal Iduna Group](https://www.signal-iduna.de/) | [Jonas Thomsen](https://github.com/JoThomsen) | Developer Portal, documentation, monitoring, service catalog for our insurance ecosystem |
|
||||
| [Tradeshift](https://www.tradeshift.com/) | [Soren Mathiasen](https://github.com/sorenmat) | Developer Portal: documentation, monitoring, service templates, service catalog for our micro services |
|
||||
| [Unity](https://unity.com) | [Ted Cordery](https://github.com/TeddyBallGame) | A centralized service catalog with documentation for our service engineers. |
|
||||
| [PicPay](https://www.picpay.com) | [Luis Baroni](https://github.com/lcsbaroni), [Renata Poluceno](https://github.com/renatapoluceno), [PicPay](https://github.com/picpay) | Developer portal for building services throught templates, service catalog with ownership of services, documentation and metrics providing autonomy and visibility for all. |
|
||||
| [Epic Games](https://www.epicgames.com) | [Brian Jung](https://github.com/brian-at-epic), [Jeff Goldian](https://github.com/jeffgoldian-Epic) | Developer Portal: Service Catalog, Documentation, Software Templates and more making our internal teams' lives easier! |
|
||||
| [Globo](https://globo.com) | [Carlos Gusmão](https://github.com/caeugusmao), [Guilherme Vierno](https://github.com/vierno), [Denis Aoki](https://github.com/dnsaoki2), [Maycon Dionisio](https://github.com/MayconDionisio), | Reduce the friction of accessing the information engineers need about Globo's digital services through a coherent and centralized experience. |
|
||||
| [QBE](https://www.qbe.com/) | [Daniel Steel](https://github.com/danielsteelqbe), [Pete Jespers](https://github.com/petejespersqbe) | Developer portal allowing our global teams to explore and create applications, documentation and cloud infrastructure easily and quickly 🚀 |
|
||||
| [GoTo](https://www.goto.com) | [Lorenzo Orsatti](https://github.com/lorsatti) | Improve onboarding experience of new developers. Discover faster and painlessly developer documentation, API definitions and team information. Provide useful dev metrics in a central place. Provide easy-to-use templates for new services. |
|
||||
| [Telstra](https://www.telstra.com.au) | [@kiranpatel11](https://github.com/kiranpatel11), [JasonC](https://github.com/JasonC17) | Primary usage: software catalog and templates<br/> Emerging usage : TechDocs, Explore Ecosystem, TechRadar, etc |
|
||||
| [Mosaico](https://www.mosaico.com.br/) | [Wédney Yuri](https://github.com/wedneyyuri),[@tino.milton](https://github.com/miltonjacomini) | A centralized service catalog of our documentation for our service engineers. |
|
||||
| [Mox Bank](https://www.mox.com/) | [Nick Laqua](https://github.com/nick-laqua-dragon) | "Single pane of glass" developer portal for providing a best-in-class developer experience to our product teams and making Mox the best tech environment in Hongkong 🥰🚀 |
|
||||
| [Keyloop](https://www.keyloop.com/) | [Andre Wanlin](https://github.com/awanlin) | Future-motive Developer Portal to help our teams create technology to make everything about buying and owning a car better. 🚗 |
|
||||
| [Simply Business](https://sbtech.simplybusiness.co.uk/) | [@addersuk](https://github.com/addersuk), [@LightningStairs](https://github.com/LightningStairs), [@punitcse](https://github.com/punitcse), [@moltenice](https://github.com/moltenice) | Central developer portal to access everything a developer needs such as docs, internal service catalog, and the ability to quickly create a new service from a template. Internally developed Backstage plugins allow us to customise the experience to how we work. |
|
||||
| [Overwolf](https://www.overwolf.com) | [@tomwolfgang](https://github.com/tomwolfgang) | Dev portal - software catalog, tech-docs, scaffolding |
|
||||
| [Hotmart](https://www.hotmart.com) | [@fabioviana-hotmart](https://github.com/fabioviana-hotmart) | The main Developers Portal to centralize docs, applications and technical metrics. |
|
||||
| [EF Education First](https://www.ef.com) | [Daan Boerlage](https://github.com/runebaas), [Rafał Nowosielski](https://github.com/rnowosielski) | Our developer portal - primarily used for cataloging and scaffolding with the ambition to expand with more feature adoptions over time |
|
||||
| [Power Home Remodeling](https://www.techatpower.com) | [Ben Langfeld](https://github.com/benlangfeld) | Developer portal to our internal services, build on open-source software (including Kubernetes) in our own datacenters. Our Portal allows our team members to navigate inherant complexity and standardise. |
|
||||
| [Livspace](https://www.livspace.com) | [Praveen Kumar](https://github.com/praveen-livspace) | Developer portal, service catalog, tech docs, API docs and plugins |
|
||||
| [Just Eat Takeaway](https://www.justeattakeaway.com) | [Kim Wilson](https://github.com/kwilson541) | Our developer portal which centralises applications, reduces cognitive load and provides teams insights. |
|
||||
| [Hopin](https://hopin.com) | [Vladimir Glafirov](https://github.com/vglafirov), [Chloe Lee](https://github.com/msfuko) | Developer portal to streamline the development practices. Integrated with service catalog, software templates, application monitoring, tech docs and plugins. |
|
||||
| [HBO Max](https://hbomax.com) | [@mdb](https://github.com/mdb), [@nesta219](https://github.com/nesta219), [@nmische](https://github.com/nmische), [@hbomark](https://github.com/hbomark) | Developer portal hosting service catalog and API documentation, as well as cloud infrastructure details, operational visibility tools, and a custom plugin for browsing notable platform change events, such as deployments and configuration updates. |
|
||||
| [RCHLO](https://www.riachuelo.com.br) & [MIDWAY](https://www.midway.com.br) | [@marcosborges](https://github.com/marcosborges), [@defaultbr](https://github.com/defaultbr) | Self-Service Platform |
|
||||
| [HP Inc](https://www.hp.com) | [Damon Kaswell](https://github.com/dekoding) | DevEx engagement hub (dev portal: docs, standards, Q&A) and extensive assets catalog (APIs, services, code, data, etc.) for the pan-HP internal developer community. |
|
||||
| [VMware](https://www.vmware.com) | [@mpriamo](https://github.com/mpriamo), [@krisapplegate](https://github.com/krisapplegate) | Part of [Tanzu Application Platform](https://docs.vmware.com/en/VMware-Tanzu-Application-Platform/index.html) offering; internal developer portal |
|
||||
| [Ualá](https://www.uala.com.ar/) | [Santiago Bernal](https://github.com/sabernal) | Initial work being done to centralize documentation for all our microservices and APIs, as well as scaffolding new services and tracking code quality |
|
||||
| [IKEA IT AB](https://www.ingka.com) | [@bjornramberg](https://github.com/bjornramberg), [@supriyachitale](https://github.com/supriyachitale) | Supporting engineers at scale with self serve access and connecting the dots of our engineering platform and services, enabling product teams to move faster and go further, and unleashing innovation, reuse and co-creation across the organisation. |
|
||||
| [Invitae](https://www.invitae.com/en) | [@ryan-hanchett](https://github.com/ryan-hanchett), [@gmandler42](https://github.com/gmandler42) | Centralized Developer Experience portal, putting all of our tooling behind a single pane of glass and creating a living service catalog. |
|
||||
| [Fortum](https://www.fortum.com/) | [@brunoamaroalmeida](https://github.com/brunoamaroalmeida), [@dhaval-vithalani](https://github.com/dhaval-vithalani), [@sunilkumarmohanty](https://github.com/sunilkumarmohanty) | A central portal containing information about our applications, services, processes and other software assets to be used by Fortum software engineering community. |
|
||||
| [Procore](https://www.procore.com/jobs/engineering) | [@shayon](https://github.com/Shayon), [@jamesdabbs-procore](https://github.com/jamesdabbs-procore) | Developer portal - centralized software catalog and templates to enable self serve and reduce cognitive load. |
|
||||
| [Bradesco](https://banco.bradesco/html/classic/sobre/index.shtm) | [@danilosoarescardoso](https://github.com/danilosoarescardoso) | Centralized developer portal with software catalog, software templates, techdocs and some plugins from community and by our own. |
|
||||
| [SeatGeek](https://seatgeek.com) | [@zhammer](https://github.com/zhammer) | Software catalog and documentation platform |
|
||||
| [Grupo Boticário](https://www.grupoboticario.com.br/) | [@chicoribas](https://github.com/chicoribas), [@fndiaz](https://github.com/fndiaz), [@digogid](https://github.com/digogid), [@manumbs](https://github.com/manumbs), [@haooliveira84](https://github.com/haooliveira84), [@Rhullyam](https://github.com/Rhullyam) | Centralized developer portal with catalog, documentation, and software templates to build a ready to go application, including pipelines (CI/CD) and cloud services provisioning |
|
||||
| [Snyk](https://snyk.io/) | [@robcresswell](https://github.com/robcresswell) | Developer portal for software discovery, API documentation and templating |
|
||||
| [Stilingue](https://www.stilingue.com.br/) | [@stilingue-inteligencia-artificial](https://github.com/Stilingue-IA), [@bbviana](https://github.com/bbviana) | Developer portal, services catalog and centralization of metrics from Grafana, Sentry and GCP. Furthermore, centralization of documentation and infra details like DNS, Network services, SSL and so on. |
|
||||
| [TUI Group](https://www.tuigroup.com/) | [Simon Stamm](https://github.com/simonstamm), [Christian Rudolph](https://github.com/ChrisRu82) | Developer portal for all engineer to provide discoverability to all internal components, APIs, documentation and to scaffold templates with integrations to our internal tools extended by plugins from our community. |
|
||||
| [Alliander](https://www.alliander.com/) | [@leon-vg](https://github.com/leon-vg), [@gieljl](https://github.com/gieljl), [@niekteg](https://github.com/niekteg) | Developer portal - software catalog, technical documentation, software templates, tech radar and exploration of used tools/services |
|
||||
| Organization | Contact | Description of Use |
|
||||
| --------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| [Spotify](https://www.spotify.com) | [@leemills83](https://github.com/leemills83) | Main interface towards all of Spotify's infrastructure and technical documentation. |
|
||||
| [bol.com](https://www.bol.com) | [@sagacity](https://github.com/sagacity) | Initial work being done to unify platform tooling. |
|
||||
| [DFDS](https://www.dfds.com) | [@carlsendk](https://github.com/carlsendk) | V2 self-service platform. |
|
||||
| [Roadie](https://roadie.io) | [@dtuite](https://github.com/dtuite) | Hosted, managed Backstage with easy set-up |
|
||||
| [Roku](https://www.roku.com) | [@timurista](https://github.com/timurista) | Initial work on Cloud engineering service platform. |
|
||||
| [SDA SE](https://sda.se) | [@dschwank](https://github.com/dschwank), [@iammnils](https://github.com/iammnils) | Central place for developing and sharing services in our insurance ecosystem. |
|
||||
| [H-E-B](https://www.heb.com) | [@german-j-rodriguez](https://github.com/german-j-rodriguez) | Initial work on Engineering Portal service platform. |
|
||||
| [American Airlines](https://www.aa.com) | [@paulpach](https://github.com/paulpach) | Central place for developers to develop and maintain applications |
|
||||
| [Kiwi.com](https://kiwi.com) | [@aexvir](https://github.com/aexvir) | Replacing the frontend of [The Zoo](https://github.com/kiwicom/the-zoo), their service registry. |
|
||||
| [Voi](https://www.voiscooters.com/) | [@K-Phoen](https://github.com/K-Phoen) | Developer portal, main gateway to our infrastructure, documentation and internal tooling. |
|
||||
| [Talkdesk](https://www.talkdesk.com) | [@jaime-talkdesk](https://github.com/jaime-talkdesk) | Initial work for Engineering Portal and Self Provisioning to R&D |
|
||||
| [Wealthsimple](https://www.wealthsimple.com) | [@andrewthauer](https://github.com/andrewthauer) | Developer portal, service catalog, documentation and tooling |
|
||||
| [Grab](https://www.grab.com) | [@althafh](https://github.com/althafh) | Initial work as a unified interface for all of Grab's internal tooling |
|
||||
| [Telenor Sweden](https://www.telenor.se) | [@O5ten](https://github.com/O5ten) | Building a developer portal for scaffolding projects towards our unified build environment and microservice stacks |
|
||||
| [Fiverr](https://www.fiverr.com) | [@nirga](https://github.com/nirga) | Unifying separate tools that developers are using today (i.e. monitoring, dead letter queues management, etc.) into a single platform. |
|
||||
| [Zalando SE](https://www.zalando.de) | [@leviferreira](https://github.com/leviferreira) | Building V2 of the Internal Development Portal. |
|
||||
| [LegalZoom](https://legalzoom.com) | [@backjo](https://github.com/backjo) | Developer portal - hub for all engineering projects and metadata. |
|
||||
| [Expedia Group](https://www.expediagroup.com) | [Mike Turner](mailto:miturner@expediagroup.com), [Sneha Kumar](mailto:snkumar@expediagroup.com), [@guillermomanzo](https://github.com/guillermomanzo), [Erik Lindgren](https://github.com/lindgren) | EG Common Developer Toolkit |
|
||||
| [Paddle.com](https://paddle.com) | [Ioannis Georgoulas](https://github.com/geototti21) | Developer portal (Tech Docs, Service Catalog, Internal Tooling), we use vanilla Backstage FE and custom BE implementation in Go |
|
||||
| [Acast.com](https://acast.com) | [Olle Lundberg](https://github.com/lndbrg) | Developer portal with tech docs, service catalog and a bunch of other internal tooling |
|
||||
| [Lunar](https://lunar.app) | [Jacob Valdemar](https://github.com/JacobValdemar) | Internal developer portal for service overview and insights, API documentation, technical guides, onboarding guides and RFC's. |
|
||||
| [Trendyol](https://trendyol.com) | [Gamze Senturk](https://github.com/gmzsenturk), [Mert Can Bilgic](https://github.com/mertcb) | The Developer Portal has been called `Pandora`. Provides an overview of Trendyol tech ecosystem. TechDocs, Catalog, Custom Plugins and Theme. |
|
||||
| [Peloton](https://www.onepeloton.com/) | [Jim Haughwout](https://github.com/JimHaughwout) | Creating our first developer portal and tech-docs. Exploring Service Catalog, Tech Insights and Cost Insights as well. |
|
||||
| [TELUS](https://telus.com) | [Seb Barre](https://github.com/sbarre) | The Go-to place to find answers about development and delivery at TELUS. |
|
||||
| [Brex](https://www.brex.com/) | [Vamsi Chitters](https://github.com/vamsikc) | A centralized UI to understand how a service fits in the whole Brex architecture and manage a team’s engineering dependencies. |
|
||||
| [Oriflame](https://www.oriflame.com/) | [Oriflame](https://github.com/oriflame) | Internal developer portal for services, single page apps and packages overview, API documentation, technical guides, tech-radar and more. |
|
||||
| [Booz Allen Hamilton](https://www.boozallen.com/) | [Jason Miller](https://github.com/JasonMiller-BAH) | Developer portal for a full-stack software development ecosystem that accelerates consistent and repeatable Modern Software Development practices for internal innovation and investments. |
|
||||
| [Netflix](https://www.netflix.com/) | [bleathem](https://github.com/bleathem) | Our Backstage implementation will be the front door to a unified experience connecting our internal platform products across important workflows with integrated knowledge and support. |
|
||||
| [b.well](https://www.icanbwell.com/) | [Jacob Rosales](https://github.com/jrosales) | Foundation for our engineering portal and cloud insights. |
|
||||
| [PagerDuty](https://www.pagerduty.com/) | [Mark Shaw](https://github.com/markshawtoronto) | Developer portal, initially focused on software templates and tech-docs. |
|
||||
| [MoonShiner](https://moonshiner.at) | [Fabian Hippmann](https://github.com/FabianHippmann) | Developer portal - helps us keep track of our customer projects, onboard new developers & improve our development process 🌕🚀🧑🚀 |
|
||||
| [FundApps](https://www.fundapps.co/) | [Elliot Greenwood](https://github.com/egnwd) | Developer Portal - A place for us to keep track of our projects and documentation for all services and processes |
|
||||
| [DAZN](https://dazn.com/) | [Lou Bichard](https://twitter.com/loujaybee), [Marco Crivellaro](https://github.com/crivetechie), [Alex Hollerith](mailto:alex.hollerith@dazn.com) | Ingesting all of DAZN's repos for the catalog, migrating our internal platform apps (pull request boards, release information, inner source marketplace etc) to Backstage plugins (where applicable). |
|
||||
| [HelloFresh](https://www.hellofresh.de/) | [@iammuho](https://github.com/iammuho), [@ElenaForester](https://github.com/ElenaForester), [@diegomarangoni](https://github.com/diegomarangoni) | Our developer portal at HelloFresh - Spread across an organisation of 500+ engineers globally. |
|
||||
| [FactSet](https://www.factset.com/) | [@kuangp](https://github.com/kuangp) | Developer portal to provide discoverability to all internal components, APIs, documentation, and scaffold templates with integrations to our internal infrastructure tools. |
|
||||
| [Workrise](https://www.workrise.com/) | [Michael Rode](https://github.com/michaelrode) | Developer portal, main gateway to our infrastructure, documentation and internal tooling. |
|
||||
| [RedVentures](https://www.redventures.com/) | [Chris Diaz](https://github.com/codingdiaz) | Developer portal that brings everything an engineer needs to provide value into a single pane of glass. |
|
||||
| [MavTek](https://www.mavtek.com/) | [@fgascon](https://github.com/fgascon) | Developer portal focused on standardizing practices, centralizing documentation and streamlining developer practices. |
|
||||
| [QuintoAndar](https://www.quintoandar.com.br/) | [@quintoandar](https://github.com/quintoandar) | Developer portal, services catalog and centralization of service metrics. |
|
||||
| [empathy.co](https://empathy.co/) | [@guillermotti](https://github.com/guillermotti) | Developer portal for tech docs, service catalog, plugin discovery and much more. |
|
||||
| [creditas.com](https://creditas.com/) | [@aureliosaraiva](https://github.com/aureliosaraiva) [@Creditas](https://github.com/creditas) | Centralization of all services, standards, documentation, etc. We started the deployment process. |
|
||||
| [Prisjakt](https://www.prisjakt.nu) / [PriceSpy](https://pricespy.co.uk) | [@kennylindahl](https://github.com/kennylindahl) | Internal developer portal - Documentation, scaffolding, software catalog, TechRadar, Gitlab org data integration |
|
||||
| [Powerspike](https://powerspike.tv/) | [@trelore](https://github.com/trelore) | Developer portal for documentation of core libraries and repositories. |
|
||||
| [2U](https://2u.com) | [Andrew Thal](https://github.com/athal7) | Development team home-base, promoting service discoverability, resource dependencies, and tech radar |
|
||||
| [Taxfix](https://taxfix.de/) | [Sami Ur Rehman](https://github.com/samiurrehman92) | Developer's portal with software catalog at it's core. Hosts API Specs, Tech Docs, Tech Radar and some custom plugins. |
|
||||
| [Busuu](https://busuu.com/) | [Adam Tester](https://github.com/adamtester) | Developer portal with service catalog, API docs, Event docs, service templating, and cost insights. |
|
||||
| [Loadsmart](https://loadsmart.com/) | [Loadsmart](https://github.com/loadsmart) | Improve services visibility and operations for service owners and developers. |
|
||||
| [Monzo](https://monzo.com/) | [@WillSewell](https://github.com/WillSewell), [@joechrisellis](https://github.com/joechrisellis) | Developer portal showing metadata and docs for over 2000 microservices. We have built a number of plugins such as a UI for our system to measure [software excellence](https://monzo.com/blog/2021/09/15/how-we-measure-software-excellence), and a UI to show deployment and config change events. |
|
||||
| [Vaimo](https://www.vaimo.com) | [@vaimo-magnus](https://github.com/vaimo-magnus) | Developer Portal for our developers at Vaimo, currently docs and self-service towards our internal PaaS based on k8s. Plans to extend the catalog into Projects, Environments etc |
|
||||
| [Wayfair](https://www.wayfair.com) | [@fransan](https://github.com/fransan), [@errskipower](https://github.com/errskipower), [@hrrs](https://github.com/hrrs) | Developer portal for service catalog, technical documentation, and APIs. |
|
||||
| [CircleHD](https://www.circlehd.com) | [@circlehddev](https://github.com/circlehddev) | Developer Portal for internal dev team across the globe |
|
||||
| [CastDesk](https://castdesk.com) | [@circlehddev](https://github.com/circlehddev) | Developer Portal for internal dev team across the globe |
|
||||
| [Santagostino](https://santagostino.it) | [@santagostino](https://github.com/santagostino) | Developer portal, gateway to our infrastructure, documentation, service catalog and internal tooling. |
|
||||
| [Peak](https://peak.ai) | [Luke Beamish](https://github.com/lukebeamish-peak) | Developer portal for all internal engineers to access documentation and tooling. |
|
||||
| [Gelato](https://gelato.com/) | [Dmitry Makarenko](https://github.com/dmitry-makarenko-gelato) | Developer portal: documentation, service templates, org structure, service catalog, plugins for integration with internal and third-party systems🚀. |
|
||||
| [GoCardless](https://gocardless.com/) | [James Turley](https://github.com/tragiclifestories) | Developer portal: documentation, service templates, org structure, service catalog, plugins for integration with internal systems. |
|
||||
| [Box](https://www.box.com) | [@kielosz](https://github.com/kielosz), [@jluk-box](https://github.com/jluk-box), [@ptychu](https://github.com/ptychu), [@alexrybch](https://github.com/alexrybch), [@szubster](https://github.com/szubster) | Developer portal for service catalog, integration with internal systems, new service onboarding. |
|
||||
| [Bazaarvoice](https://www.bazaarvoice.com) | [@niallmccullagh](https://github.com/niallmccullagh) | Developer portal for service catalog and scaffolds, publishing Github docs and API documentation, visualising our internal tech radar and our product engineering org structure. |
|
||||
| [Krateo PlatformOps](https://www.krateo.io) | [@projectkerberus](https://github.com/projectkerberus) | A multi-cloud control plane to create, manage and deploy any kind of resource easily and centrally via a Developer Portal that centralizes via a self-service catalog the templating and ownership of services, the available documentation, the overview of the components that compose an entire domain and all the data of the service lifecycle. |
|
||||
| [Adevinta](https://www.adevinta.com) | [Ray Sinnema](https://github.com/RemonSinnema) | Showcase shared services to our internal customers. |
|
||||
| [Splunk](https://www.splunk.com) | [@tonytamsf](https://github.com/tonytamsf) | Developer portal as a centralized place to find people, services, documentation, escalation policies and give bravos. This portal is also being used as a centralized search engine for engineering specific documentation. |
|
||||
| [SoundCloud](https://www.soundcloud.com) | [Julio Zynger](https://github.com/julioz) | Developer portal as a [humane registry](https://martinfowler.com/bliki/HumaneRegistry.html) for the organization: catalog of people, services, documentation, feature toggles, escalation policies, etc. |
|
||||
| [Volvofinans Bank](https://www.volvofinans.se) | [Johan Hammar](https://github.com/johanhammar) | Developer portal enabling engineers to manage and explore software and documentation. |
|
||||
| [Palo Alto Networks](https://www.paloaltonetworks.com) | [Jeremy Guarini](https://github.com/jeremyguarini), [Brian Lomeland](https://github.com/bbbmmmlll), [Palo Alto Networks](https://github.com/PaloAltoNetworks) | Developer portal, service catalog, documentation and tooling |
|
||||
| [Signal Iduna Group](https://www.signal-iduna.de/) | [Jonas Thomsen](https://github.com/JoThomsen) | Developer Portal, documentation, monitoring, service catalog for our insurance ecosystem |
|
||||
| [Tradeshift](https://www.tradeshift.com/) | [Soren Mathiasen](https://github.com/sorenmat) | Developer Portal: documentation, monitoring, service templates, service catalog for our micro services |
|
||||
| [Unity](https://unity.com) | [Ted Cordery](https://github.com/TeddyBallGame) | A centralized service catalog with documentation for our service engineers. |
|
||||
| [PicPay](https://www.picpay.com) | [Luis Baroni](https://github.com/lcsbaroni), [Renata Poluceno](https://github.com/renatapoluceno), [PicPay](https://github.com/picpay) | Developer portal for building services throught templates, service catalog with ownership of services, documentation and metrics providing autonomy and visibility for all. |
|
||||
| [Epic Games](https://www.epicgames.com) | [Brian Jung](https://github.com/brian-at-epic), [Jeff Goldian](https://github.com/jeffgoldian-Epic) | Developer Portal: Service Catalog, Documentation, Software Templates and more making our internal teams' lives easier! |
|
||||
| [Globo](https://globo.com) | [Carlos Gusmão](https://github.com/caeugusmao), [Guilherme Vierno](https://github.com/vierno), [Denis Aoki](https://github.com/dnsaoki2), [Maycon Dionisio](https://github.com/MayconDionisio), | Reduce the friction of accessing the information engineers need about Globo's digital services through a coherent and centralized experience. |
|
||||
| [QBE](https://www.qbe.com/) | [Daniel Steel](https://github.com/danielsteelqbe), [Pete Jespers](https://github.com/petejespersqbe) | Developer portal allowing our global teams to explore and create applications, documentation and cloud infrastructure easily and quickly 🚀 |
|
||||
| [GoTo](https://www.goto.com) | [Lorenzo Orsatti](https://github.com/lorsatti) | Improve onboarding experience of new developers. Discover faster and painlessly developer documentation, API definitions and team information. Provide useful dev metrics in a central place. Provide easy-to-use templates for new services. |
|
||||
| [Telstra](https://www.telstra.com.au) | [@kiranpatel11](https://github.com/kiranpatel11), [JasonC](https://github.com/JasonC17) | Primary usage: software catalog and templates<br/> Emerging usage : TechDocs, Explore Ecosystem, TechRadar, etc |
|
||||
| [Mosaico](https://www.mosaico.com.br/) | [Wédney Yuri](https://github.com/wedneyyuri),[@tino.milton](https://github.com/miltonjacomini) | A centralized service catalog of our documentation for our service engineers. |
|
||||
| [Mox Bank](https://www.mox.com/) | [Nick Laqua](https://github.com/nick-laqua-dragon) | "Single pane of glass" developer portal for providing a best-in-class developer experience to our product teams and making Mox the best tech environment in Hongkong 🥰🚀 |
|
||||
| [Keyloop](https://www.keyloop.com/) | [Andre Wanlin](https://github.com/awanlin) | Future-motive Developer Portal to help our teams create technology to make everything about buying and owning a car better. 🚗 |
|
||||
| [Simply Business](https://sbtech.simplybusiness.co.uk/) | [@addersuk](https://github.com/addersuk), [@LightningStairs](https://github.com/LightningStairs), [@punitcse](https://github.com/punitcse), [@moltenice](https://github.com/moltenice) | Central developer portal to access everything a developer needs such as docs, internal service catalog, and the ability to quickly create a new service from a template. Internally developed Backstage plugins allow us to customise the experience to how we work. |
|
||||
| [Overwolf](https://www.overwolf.com) | [@tomwolfgang](https://github.com/tomwolfgang) | Dev portal - software catalog, tech-docs, scaffolding |
|
||||
| [Hotmart](https://www.hotmart.com) | [@fabioviana-hotmart](https://github.com/fabioviana-hotmart) | The main Developers Portal to centralize docs, applications and technical metrics. |
|
||||
| [EF Education First](https://www.ef.com) | [Daan Boerlage](https://github.com/runebaas), [Rafał Nowosielski](https://github.com/rnowosielski) | Our developer portal - primarily used for cataloging and scaffolding with the ambition to expand with more feature adoptions over time |
|
||||
| [Power Home Remodeling](https://www.techatpower.com) | [Ben Langfeld](https://github.com/benlangfeld) | Developer portal to our internal services, build on open-source software (including Kubernetes) in our own datacenters. Our Portal allows our team members to navigate inherant complexity and standardise. |
|
||||
| [Livspace](https://www.livspace.com) | [Praveen Kumar](https://github.com/praveen-livspace) | Developer portal, service catalog, tech docs, API docs and plugins |
|
||||
| [Just Eat Takeaway](https://www.justeattakeaway.com) | [Kim Wilson](https://github.com/kwilson541) | Our developer portal which centralises applications, reduces cognitive load and provides teams insights. |
|
||||
| [Hopin](https://hopin.com) | [Vladimir Glafirov](https://github.com/vglafirov), [Chloe Lee](https://github.com/msfuko) | Developer portal to streamline the development practices. Integrated with service catalog, software templates, application monitoring, tech docs and plugins. |
|
||||
| [HBO Max](https://hbomax.com) | [@mdb](https://github.com/mdb), [@nesta219](https://github.com/nesta219), [@nmische](https://github.com/nmische), [@hbomark](https://github.com/hbomark) | Developer portal hosting service catalog and API documentation, as well as cloud infrastructure details, operational visibility tools, and a custom plugin for browsing notable platform change events, such as deployments and configuration updates. |
|
||||
| [RCHLO](https://www.riachuelo.com.br) & [MIDWAY](https://www.midway.com.br) | [@marcosborges](https://github.com/marcosborges), [@defaultbr](https://github.com/defaultbr) | Self-Service Platform |
|
||||
| [HP Inc](https://www.hp.com) | [Damon Kaswell](https://github.com/dekoding) | DevEx engagement hub (dev portal: docs, standards, Q&A) and extensive assets catalog (APIs, services, code, data, etc.) for the pan-HP internal developer community. |
|
||||
| [VMware](https://www.vmware.com) | [@mpriamo](https://github.com/mpriamo), [@krisapplegate](https://github.com/krisapplegate) | Part of [Tanzu Application Platform](https://docs.vmware.com/en/VMware-Tanzu-Application-Platform/index.html) offering; internal developer portal |
|
||||
| [Ualá](https://www.uala.com.ar/) | [Santiago Bernal](https://github.com/sabernal) | Initial work being done to centralize documentation for all our microservices and APIs, as well as scaffolding new services and tracking code quality |
|
||||
| [IKEA IT AB](https://www.ingka.com) | [@bjornramberg](https://github.com/bjornramberg), [@supriyachitale](https://github.com/supriyachitale) | Supporting engineers at scale with self serve access and connecting the dots of our engineering platform and services, enabling product teams to move faster and go further, and unleashing innovation, reuse and co-creation across the organisation. |
|
||||
| [Invitae](https://www.invitae.com/en) | [@ryan-hanchett](https://github.com/ryan-hanchett), [@gmandler42](https://github.com/gmandler42) | Centralized Developer Experience portal, putting all of our tooling behind a single pane of glass and creating a living service catalog. |
|
||||
| [Fortum](https://www.fortum.com/) | [@brunoamaroalmeida](https://github.com/brunoamaroalmeida), [@dhaval-vithalani](https://github.com/dhaval-vithalani), [@sunilkumarmohanty](https://github.com/sunilkumarmohanty) | A central portal containing information about our applications, services, processes and other software assets to be used by Fortum software engineering community. |
|
||||
| [Procore](https://www.procore.com/jobs/engineering) | [@shayon](https://github.com/Shayon), [@jamesdabbs-procore](https://github.com/jamesdabbs-procore) | Developer portal - centralized software catalog and templates to enable self serve and reduce cognitive load. |
|
||||
| [Bradesco](https://banco.bradesco/html/classic/sobre/index.shtm) | [@danilosoarescardoso](https://github.com/danilosoarescardoso) | Centralized developer portal with software catalog, software templates, techdocs and some plugins from community and by our own. |
|
||||
| [SeatGeek](https://seatgeek.com) | [@zhammer](https://github.com/zhammer) | Software catalog and documentation platform |
|
||||
| [Grupo Boticário](https://www.grupoboticario.com.br/) | [@chicoribas](https://github.com/chicoribas), [@fndiaz](https://github.com/fndiaz), [@digogid](https://github.com/digogid), [@manumbs](https://github.com/manumbs), [@haooliveira84](https://github.com/haooliveira84), [@Rhullyam](https://github.com/Rhullyam) | Centralized developer portal with catalog, documentation, and software templates to build a ready to go application, including pipelines (CI/CD) and cloud services provisioning |
|
||||
| [Snyk](https://snyk.io/) | [@robcresswell](https://github.com/robcresswell) | Developer portal for software discovery, API documentation and templating |
|
||||
| [Stilingue](https://www.stilingue.com.br/) | [@stilingue-inteligencia-artificial](https://github.com/Stilingue-IA), [@bbviana](https://github.com/bbviana) | Developer portal, services catalog and centralization of metrics from Grafana, Sentry and GCP. Furthermore, centralization of documentation and infra details like DNS, Network services, SSL and so on. |
|
||||
| [TUI Group](https://www.tuigroup.com/) | [Simon Stamm](https://github.com/simonstamm), [Christian Rudolph](https://github.com/ChrisRu82) | Developer portal for all engineer to provide discoverability to all internal components, APIs, documentation and to scaffold templates with integrations to our internal tools extended by plugins from our community. |
|
||||
| [Alliander](https://www.alliander.com/) | [@leon-vg](https://github.com/leon-vg), [@gieljl](https://github.com/gieljl), [@niekteg](https://github.com/niekteg) | Developer portal - software catalog, technical documentation, software templates, tech radar and exploration of used tools/services |
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
"@backstage/plugin-code-coverage": "^0.1.29",
|
||||
"@backstage/plugin-cost-insights": "^0.11.24",
|
||||
"@backstage/plugin-explore": "^0.3.33",
|
||||
"@backstage/plugin-gcalendar": "^0.1.0",
|
||||
"@backstage/plugin-gcp-projects": "^0.3.21",
|
||||
"@backstage/plugin-github-actions": "^0.5.2",
|
||||
"@backstage/plugin-gocd": "^0.1.8",
|
||||
@@ -59,10 +60,10 @@
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
"@material-ui/lab": "4.0.0-alpha.57",
|
||||
"@octokit/rest": "^18.5.3",
|
||||
"@roadiehq/backstage-plugin-buildkite": "^1.3.8",
|
||||
"@roadiehq/backstage-plugin-github-insights": "^1.5.0",
|
||||
"@roadiehq/backstage-plugin-github-pull-requests": "^1.4.0",
|
||||
"@roadiehq/backstage-plugin-travis-ci": "^1.3.6",
|
||||
"@roadiehq/backstage-plugin-buildkite": "^1.3.8",
|
||||
"history": "^5.0.0",
|
||||
"prop-types": "^15.7.2",
|
||||
"react": "^17.0.2",
|
||||
|
||||
@@ -76,7 +76,7 @@ import { hot } from 'react-hot-loader/root';
|
||||
import { Navigate, Route } from 'react-router';
|
||||
import { apis } from './apis';
|
||||
import { entityPage } from './components/catalog/EntityPage';
|
||||
import { HomePage } from './components/home/HomePage';
|
||||
import { homePage } from './components/home/HomePage';
|
||||
import { Root } from './components/Root';
|
||||
import { LowerCaseValuePickerFieldExtension } from './components/scaffolder/customScaffolderExtensions';
|
||||
import { searchPage } from './components/search/SearchPage';
|
||||
@@ -132,7 +132,7 @@ const routes = (
|
||||
<Navigate key="/" to="catalog" />
|
||||
{/* TODO(rubenl): Move this to / once its more mature and components exist */}
|
||||
<Route path="/home" element={<HomepageCompositionRoot />}>
|
||||
<HomePage />
|
||||
{homePage}
|
||||
</Route>
|
||||
<Route path="/catalog" element={<CatalogIndexPage />} />
|
||||
<Route
|
||||
|
||||
@@ -25,6 +25,7 @@ import {
|
||||
} from '@backstage/plugin-home';
|
||||
import { Content, Header, Page } from '@backstage/core-components';
|
||||
import { HomePageSearchBar } from '@backstage/plugin-search';
|
||||
import { HomePageCalendar } from '@backstage/plugin-gcalendar';
|
||||
import Grid from '@material-ui/core/Grid';
|
||||
import React from 'react';
|
||||
|
||||
@@ -47,7 +48,7 @@ const clockConfigs: ClockConfig[] = [
|
||||
},
|
||||
];
|
||||
|
||||
export const HomePage = () => (
|
||||
export const homePage = (
|
||||
<Page themeId="home">
|
||||
<Header title={<WelcomeTitle />} pageTitleOverride="Home">
|
||||
<HeaderWorldClock clockConfigs={clockConfigs} />
|
||||
@@ -100,6 +101,9 @@ export const HomePage = () => (
|
||||
]}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={4}>
|
||||
<HomePageCalendar />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Content>
|
||||
</Page>
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
module.exports = require('@backstage/cli/config/eslint-factory')(__dirname);
|
||||
@@ -0,0 +1,28 @@
|
||||
# Google calendar plugin
|
||||
|
||||
Plugin displays events from google calendar
|
||||
|
||||
## Getting started
|
||||
|
||||
The plugin exports `HomePageCalendar` widget for the Homepage.
|
||||
If your homepage is not static JSX add `gcalendarApiRef` to the App's `apis.ts`:
|
||||
|
||||
```ts
|
||||
import {
|
||||
GCalendarApiClient,
|
||||
gcalendarApiRef,
|
||||
} from '@backstage/plugin-gcalendar';
|
||||
|
||||
export const apis = [
|
||||
// ...
|
||||
createApiFactory({
|
||||
api: gcalendarApiRef,
|
||||
deps: { authApi: googleAuthApiRef, fetchApi: fetchApiRef },
|
||||
factory: deps => new GCalendarApiClient(deps),
|
||||
}),
|
||||
];
|
||||
```
|
||||
|
||||
You can also serve the plugin in isolation by running `yarn start` in the plugin directory.
|
||||
This method of serving the plugin provides quicker iteration speed and a faster startup and hot reloads.
|
||||
It is only meant for local development, and the setup for it can be found inside the [/dev](./dev) directory.
|
||||
@@ -0,0 +1,88 @@
|
||||
## API Report File for "@backstage/plugin-gcalendar"
|
||||
|
||||
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
|
||||
|
||||
```ts
|
||||
/// <reference types="@maxim_mazurok/gapi.client.calendar" />
|
||||
/// <reference types="react" />
|
||||
|
||||
import { ApiRef } from '@backstage/core-plugin-api';
|
||||
import { BackstagePlugin } from '@backstage/core-plugin-api';
|
||||
import { FetchApi } from '@backstage/core-plugin-api';
|
||||
import { OAuthApi } from '@backstage/core-plugin-api';
|
||||
import { RouteRef } from '@backstage/core-plugin-api';
|
||||
|
||||
// Warning: (ae-missing-release-tag) "EventAttendee" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
export type EventAttendee = gapi.client.calendar.EventAttendee;
|
||||
|
||||
// Warning: (ae-missing-release-tag) "GCalendar" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
export type GCalendar = gapi.client.calendar.CalendarListEntry;
|
||||
|
||||
// Warning: (ae-missing-release-tag) "GCalendarApiClient" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
export class GCalendarApiClient {
|
||||
// Warning: (ae-forgotten-export) The symbol "Options" needs to be exported by the entry point index.d.ts
|
||||
constructor(options: Options);
|
||||
// (undocumented)
|
||||
getCalendars(params?: any): Promise<{
|
||||
items: GCalendar[];
|
||||
}>;
|
||||
// (undocumented)
|
||||
getEvents(
|
||||
calendarId: string,
|
||||
params?: any,
|
||||
): Promise<{
|
||||
items: GCalendarEvent[];
|
||||
}>;
|
||||
}
|
||||
|
||||
// Warning: (ae-missing-release-tag) "gcalendarApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
export const gcalendarApiRef: ApiRef<GCalendarApiClient>;
|
||||
|
||||
// Warning: (ae-missing-release-tag) "GCalendarEvent" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
export type GCalendarEvent = gapi.client.calendar.Event &
|
||||
Pick<GCalendar, 'backgroundColor' | 'primary'> &
|
||||
Pick<EventAttendee, 'responseStatus'> & {
|
||||
calendarId?: string;
|
||||
};
|
||||
|
||||
// Warning: (ae-missing-release-tag) "gcalendarPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
export const gcalendarPlugin: BackstagePlugin<
|
||||
{
|
||||
root: RouteRef<undefined>;
|
||||
},
|
||||
{}
|
||||
>;
|
||||
|
||||
// Warning: (ae-missing-release-tag) "HomePageCalendar" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
export const HomePageCalendar: () => JSX.Element;
|
||||
|
||||
// Warning: (ae-missing-release-tag) "ResponseStatus" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
export enum ResponseStatus {
|
||||
// (undocumented)
|
||||
accepted = 'accepted',
|
||||
// (undocumented)
|
||||
declined = 'declined',
|
||||
// (undocumented)
|
||||
maybe = 'tentative',
|
||||
// (undocumented)
|
||||
needsAction = 'needsAction',
|
||||
}
|
||||
|
||||
// (No @packageDocumentation comment for this package)
|
||||
```
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { Content, Page } from '@backstage/core-components';
|
||||
import { googleAuthApiRef } from '@backstage/core-plugin-api';
|
||||
import { createDevApp } from '@backstage/dev-utils';
|
||||
import { calendarListMock, eventsMock } from './mocks';
|
||||
import { gcalendarPlugin, HomePageCalendar } from '../src/plugin';
|
||||
import { gcalendarApiRef } from '../src';
|
||||
|
||||
createDevApp()
|
||||
.registerPlugin(gcalendarPlugin)
|
||||
.registerApi({
|
||||
api: googleAuthApiRef,
|
||||
deps: {},
|
||||
factory: () =>
|
||||
({
|
||||
async getAccessToken() {
|
||||
return Promise.resolve('token');
|
||||
},
|
||||
} as unknown as typeof googleAuthApiRef.T),
|
||||
})
|
||||
.registerApi({
|
||||
api: gcalendarApiRef,
|
||||
deps: {},
|
||||
factory: () =>
|
||||
({
|
||||
async getCalendars() {
|
||||
return Promise.resolve(calendarListMock);
|
||||
},
|
||||
async getEvents() {
|
||||
return Promise.resolve({
|
||||
items: eventsMock,
|
||||
});
|
||||
},
|
||||
} as unknown as typeof gcalendarApiRef.T),
|
||||
})
|
||||
.addPage({
|
||||
element: (
|
||||
<Page themeId="home">
|
||||
<Content>
|
||||
<HomePageCalendar />
|
||||
</Content>
|
||||
</Page>
|
||||
),
|
||||
title: 'Root Page',
|
||||
})
|
||||
.render();
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { DateTime } from 'luxon';
|
||||
import { GCalendar, GCalendarEvent } from '../src/api';
|
||||
|
||||
const primaryCalendar: GCalendar = {
|
||||
id: 'calendar-1',
|
||||
summary: 'test-1@test.com',
|
||||
primary: true,
|
||||
};
|
||||
const nonPrimaryCalendar: GCalendar = {
|
||||
id: 'calendar-2',
|
||||
summary: 'test-2@test.com',
|
||||
primary: false,
|
||||
backgroundColor: '#9BF0E1',
|
||||
};
|
||||
|
||||
export const calendarListMock = {
|
||||
items: [primaryCalendar, nonPrimaryCalendar],
|
||||
};
|
||||
|
||||
export const eventsMock: GCalendarEvent[] = [...Array(3).keys()].map(i => ({
|
||||
id: i.toString(),
|
||||
summary: `Meeting ${i + 1}`,
|
||||
start: {
|
||||
dateTime: DateTime.now()
|
||||
.minus({ hour: i + 1 })
|
||||
.toISO(),
|
||||
timeZone: 'Europe/London',
|
||||
},
|
||||
end: {
|
||||
dateTime: DateTime.now().minus({ hour: i }).toISO(),
|
||||
timeZone: 'Europe/London',
|
||||
},
|
||||
description: '<h3>Dummy title</h3><p>Dummy description</p>',
|
||||
conferenceData: {
|
||||
entryPoints: [
|
||||
{
|
||||
entryPointType: 'video',
|
||||
uri: 'https://zoom.us/',
|
||||
},
|
||||
],
|
||||
},
|
||||
}));
|
||||
@@ -0,0 +1,65 @@
|
||||
{
|
||||
"name": "@backstage/plugin-gcalendar",
|
||||
"version": "0.1.0",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"license": "Apache-2.0",
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"main": "dist/index.esm.js",
|
||||
"types": "dist/index.d.ts"
|
||||
},
|
||||
"backstage": {
|
||||
"role": "frontend-plugin"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "backstage-cli package start",
|
||||
"build": "backstage-cli package build",
|
||||
"clean": "backstage-cli package clean",
|
||||
"test": "backstage-cli package test",
|
||||
"lint": "backstage-cli package lint",
|
||||
"prepack": "backstage-cli package prepack",
|
||||
"postpack": "backstage-cli package postpack"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/core-components": "^0.9.1",
|
||||
"@backstage/core-plugin-api": "^0.8.0",
|
||||
"@backstage/errors": "^0.2.2",
|
||||
"@backstage/theme": "^0.2.15",
|
||||
"@material-ui/core": "^4.12.2",
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
"@material-ui/lab": "4.0.0-alpha.57",
|
||||
"classnames": "^2.3.1",
|
||||
"cross-fetch": "^3.1.5",
|
||||
"dompurify": "^2.3.6",
|
||||
"lodash": "^4.17.21",
|
||||
"luxon": "^2.3.0",
|
||||
"material-ui-popup-state": "^1.9.3",
|
||||
"react-query": "^3.34.16",
|
||||
"react-use": "^17.2.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.13.1 || ^17.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.15.2",
|
||||
"@backstage/core-app-api": "^0.6.0",
|
||||
"@backstage/dev-utils": "^0.2.25",
|
||||
"@backstage/test-utils": "^0.3.0",
|
||||
"@testing-library/jest-dom": "^5.10.1",
|
||||
"@testing-library/react": "^11.2.5",
|
||||
"@testing-library/user-event": "^13.1.8",
|
||||
"@types/dompurify": "^2.3.3",
|
||||
"@types/gapi": "^0.0.41",
|
||||
"@types/gapi.auth2": "^0.0.56",
|
||||
"@types/gapi.client.calendar": "^3.0.10",
|
||||
"@types/jest": "^26.0.7",
|
||||
"@types/node": "^14.14.32",
|
||||
"@types/sanitize-html": "^2.6.2",
|
||||
"cross-fetch": "^3.1.5",
|
||||
"msw": "^0.35.0"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { OAuthApi, createApiRef, FetchApi } from '@backstage/core-plugin-api';
|
||||
|
||||
import { GCalendar, GCalendarEvent } from './types';
|
||||
import { ResponseError } from '@backstage/errors';
|
||||
|
||||
type Options = {
|
||||
authApi: OAuthApi;
|
||||
fetchApi: FetchApi;
|
||||
};
|
||||
|
||||
export const gcalendarApiRef = createApiRef<GCalendarApiClient>({
|
||||
id: 'plugin.gcalendar.service',
|
||||
});
|
||||
|
||||
export class GCalendarApiClient {
|
||||
private readonly authApi: OAuthApi;
|
||||
private readonly fetchApi: FetchApi;
|
||||
|
||||
constructor(options: Options) {
|
||||
this.authApi = options.authApi;
|
||||
this.fetchApi = options.fetchApi;
|
||||
}
|
||||
|
||||
private async get<T>(
|
||||
path: string,
|
||||
params: { [key in string]: any },
|
||||
): Promise<T> {
|
||||
const query = new URLSearchParams(params);
|
||||
const url = new URL(
|
||||
`${path}?${query.toString()}`,
|
||||
'https://www.googleapis.com',
|
||||
);
|
||||
const token = await this.authApi.getAccessToken();
|
||||
const response = await this.fetchApi.fetch(url.toString(), {
|
||||
headers: token ? { Authorization: `Bearer ${token}` } : {},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw await ResponseError.fromResponse(response);
|
||||
}
|
||||
|
||||
return response.json() as Promise<T>;
|
||||
}
|
||||
|
||||
public async getCalendars(params?: any) {
|
||||
return this.get<{ items: GCalendar[] }>(
|
||||
'/calendar/v3/users/me/calendarList',
|
||||
params,
|
||||
);
|
||||
}
|
||||
|
||||
public async getEvents(calendarId: string, params?: any) {
|
||||
return this.get<{ items: GCalendarEvent[] }>(
|
||||
`/calendar/v3/calendars/${encodeURIComponent(calendarId)}/events`,
|
||||
params,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
export * from './client';
|
||||
export * from './types';
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/// <reference types="gapi.auth2" />
|
||||
/// <reference types="gapi.client.calendar" />
|
||||
|
||||
export type GCalendar = gapi.client.calendar.CalendarListEntry;
|
||||
|
||||
export type EventAttendee = gapi.client.calendar.EventAttendee;
|
||||
|
||||
export type GCalendarEvent = gapi.client.calendar.Event &
|
||||
Pick<GCalendar, 'backgroundColor' | 'primary'> &
|
||||
Pick<EventAttendee, 'responseStatus'> & {
|
||||
calendarId?: string;
|
||||
};
|
||||
|
||||
export enum ResponseStatus {
|
||||
needsAction = 'needsAction',
|
||||
accepted = 'accepted',
|
||||
declined = 'declined',
|
||||
maybe = 'tentative',
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
|
||||
import { AttendeeChip } from './AttendeeChip';
|
||||
import { EventAttendee, ResponseStatus } from '../../api';
|
||||
|
||||
describe('<AttendeeChip />', () => {
|
||||
it('renders attendee email', async () => {
|
||||
const email = 'test@test.com';
|
||||
const user: EventAttendee = {
|
||||
email,
|
||||
responseStatus: ResponseStatus.needsAction,
|
||||
};
|
||||
const { queryByText } = await renderInTestApp(<AttendeeChip user={user} />);
|
||||
expect(queryByText(email)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders accepted icon', async () => {
|
||||
const email = 'test@test.com';
|
||||
const user: EventAttendee = {
|
||||
email,
|
||||
responseStatus: ResponseStatus.accepted,
|
||||
};
|
||||
const { getByTestId } = await renderInTestApp(<AttendeeChip user={user} />);
|
||||
expect(getByTestId('accepted-icon')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders declined icon', async () => {
|
||||
const email = 'test@test.com';
|
||||
const user: EventAttendee = {
|
||||
email,
|
||||
responseStatus: ResponseStatus.declined,
|
||||
};
|
||||
const { getByTestId } = await renderInTestApp(<AttendeeChip user={user} />);
|
||||
expect(getByTestId('declined-icon')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
|
||||
import { BackstageTheme } from '@backstage/theme';
|
||||
|
||||
import { Badge, Chip, makeStyles } from '@material-ui/core';
|
||||
import CancelIcon from '@material-ui/icons/Cancel';
|
||||
import CheckIcon from '@material-ui/icons/CheckCircle';
|
||||
|
||||
import { EventAttendee, ResponseStatus } from '../../api';
|
||||
|
||||
const useStyles = makeStyles((theme: BackstageTheme) => {
|
||||
const getIconColor = (responseStatus?: string) => {
|
||||
if (!responseStatus) return theme.palette.primary.light;
|
||||
|
||||
return {
|
||||
[ResponseStatus.accepted]: theme.palette.status.ok,
|
||||
[ResponseStatus.declined]: theme.palette.status.error,
|
||||
}[responseStatus];
|
||||
};
|
||||
|
||||
return {
|
||||
responseStatus: {
|
||||
color: ({ responseStatus }: { responseStatus?: string }) =>
|
||||
getIconColor(responseStatus),
|
||||
},
|
||||
badge: {
|
||||
right: 10,
|
||||
top: 5,
|
||||
'& svg': {
|
||||
height: 16,
|
||||
width: 16,
|
||||
background: '#fff',
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const ResponseIcon = ({ responseStatus }: any) => {
|
||||
if (responseStatus === ResponseStatus.accepted) {
|
||||
return <CheckIcon data-testid="accepted-icon" />;
|
||||
}
|
||||
if (responseStatus === ResponseStatus.declined) {
|
||||
return <CancelIcon data-testid="declined-icon" />;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
type AttendeeChipProps = {
|
||||
user: EventAttendee;
|
||||
};
|
||||
|
||||
export const AttendeeChip = ({ user }: AttendeeChipProps) => {
|
||||
const classes = useStyles({ responseStatus: user.responseStatus });
|
||||
|
||||
return (
|
||||
<Badge
|
||||
classes={{
|
||||
root: classes.responseStatus,
|
||||
badge: classes.badge,
|
||||
}}
|
||||
badgeContent={<ResponseIcon responseStatus={user.responseStatus} />}
|
||||
>
|
||||
<Chip
|
||||
size="small"
|
||||
variant="outlined"
|
||||
label={user.email}
|
||||
color="primary"
|
||||
/>
|
||||
</Badge>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { sortBy } from 'lodash';
|
||||
import { DateTime } from 'luxon';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
|
||||
import { InfoCard, Progress } from '@backstage/core-components';
|
||||
import { useAnalytics } from '@backstage/core-plugin-api';
|
||||
|
||||
import { Box, IconButton, Typography } from '@material-ui/core';
|
||||
import PrevIcon from '@material-ui/icons/NavigateBefore';
|
||||
import NextIcon from '@material-ui/icons/NavigateNext';
|
||||
|
||||
import {
|
||||
useCalendarsQuery,
|
||||
useEventsQuery,
|
||||
useSignIn,
|
||||
useStoredCalendars,
|
||||
} from '../../hooks';
|
||||
import calendarCardIcon from '../../icons/calendarIcon.svg';
|
||||
import { CalendarEvent } from './CalendarEvent';
|
||||
import { CalendarSelect } from './CalendarSelect';
|
||||
import { SignInContent } from './SignInContent';
|
||||
import { getStartDate } from './util';
|
||||
|
||||
export const CalendarCard = () => {
|
||||
const [date, setDate] = useState(DateTime.now());
|
||||
const analytics = useAnalytics();
|
||||
|
||||
const changeDay = (offset = 1) => {
|
||||
setDate(prev => prev.plus({ day: offset }));
|
||||
analytics.captureEvent('click', 'change date');
|
||||
};
|
||||
|
||||
const { isSignedIn, isInitialized, signIn } = useSignIn();
|
||||
|
||||
useEffect(() => {
|
||||
signIn(true);
|
||||
}, [signIn]);
|
||||
|
||||
const { isLoading: isCalendarLoading, data } = useCalendarsQuery({
|
||||
enabled: isSignedIn,
|
||||
});
|
||||
const calendars = useMemo(() => data?.items || [], [data]);
|
||||
const primaryCalendarId = calendars.find(c => c.primary === true)?.id;
|
||||
const defaultSelectedCalendars = primaryCalendarId ? [primaryCalendarId] : [];
|
||||
const [storedCalendars, setStoredCalendars] = useStoredCalendars(
|
||||
defaultSelectedCalendars,
|
||||
);
|
||||
|
||||
const { events, isLoading: isEventLoading } = useEventsQuery({
|
||||
calendars,
|
||||
selectedCalendars: storedCalendars,
|
||||
enabled: isSignedIn && calendars.length > 0,
|
||||
timeMin: date.startOf('day').toISO(),
|
||||
timeMax: date.endOf('day').toISO(),
|
||||
timeZone: date.zoneName,
|
||||
});
|
||||
|
||||
return (
|
||||
<InfoCard
|
||||
noPadding
|
||||
title={
|
||||
<Box display="flex" alignItems="center">
|
||||
<Box height={24} width={24} mr={1}>
|
||||
<img src={calendarCardIcon} alt="Google Calendar" />
|
||||
</Box>
|
||||
{isSignedIn ? (
|
||||
<>
|
||||
<IconButton onClick={() => changeDay(-1)} size="small">
|
||||
<PrevIcon />
|
||||
</IconButton>
|
||||
<IconButton onClick={() => changeDay(1)} size="small">
|
||||
<NextIcon />
|
||||
</IconButton>
|
||||
<Box mr={0.5} />
|
||||
<Typography variant="h6">
|
||||
{date.toLocaleString({
|
||||
weekday: 'short',
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
})}
|
||||
</Typography>
|
||||
|
||||
<Box flex={1} />
|
||||
|
||||
<CalendarSelect
|
||||
calendars={calendars}
|
||||
selectedCalendars={storedCalendars}
|
||||
setSelectedCalendars={setStoredCalendars}
|
||||
disabled={isCalendarLoading || !isSignedIn}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<Typography variant="h6">Agenda</Typography>
|
||||
)}
|
||||
</Box>
|
||||
}
|
||||
deepLink={{
|
||||
link: 'https://calendar.google.com/',
|
||||
title: 'Go to Calendar',
|
||||
}}
|
||||
>
|
||||
<Box>
|
||||
{(isCalendarLoading || isEventLoading || !isInitialized) && (
|
||||
<Box pt={2} pb={2}>
|
||||
<Progress variant="query" />
|
||||
</Box>
|
||||
)}
|
||||
{!isSignedIn && isInitialized && (
|
||||
<SignInContent handleAuthClick={() => signIn(false)} />
|
||||
)}
|
||||
{!isEventLoading && !isCalendarLoading && isSignedIn && (
|
||||
<Box p={1} pb={0} maxHeight={602} overflow="auto">
|
||||
{events.length === 0 && (
|
||||
<Box pt={2} pb={2}>
|
||||
<Typography align="center" variant="h6">
|
||||
No events
|
||||
</Typography>
|
||||
</Box>
|
||||
)}
|
||||
{sortBy(events, [getStartDate]).map(event => (
|
||||
<CalendarEvent
|
||||
key={`${event.calendarId}-${event.id}`}
|
||||
event={event}
|
||||
/>
|
||||
))}
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
</InfoCard>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { fireEvent } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
|
||||
import { CalendarEvent } from './CalendarEvent';
|
||||
|
||||
describe('<CalendarEvent />', () => {
|
||||
const event = {
|
||||
summary: 'Test event',
|
||||
htmlLink: '/calendar-link',
|
||||
start: {
|
||||
dateTime: '2022-02-09T10:15:00',
|
||||
},
|
||||
end: {
|
||||
dateTime: '2022-02-09T10:45:00',
|
||||
},
|
||||
conferenceData: {
|
||||
entryPoints: [{ entryPointType: 'video', uri: '/zoom-link' }],
|
||||
},
|
||||
description: 'Test description',
|
||||
attendees: [
|
||||
{
|
||||
email: 'test@test.com',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
it('should render calendar event', async () => {
|
||||
const { queryByText, queryByTestId } = await renderInTestApp(
|
||||
<CalendarEvent event={event} />,
|
||||
);
|
||||
expect(queryByText(event.summary)).toBeInTheDocument();
|
||||
expect(queryByTestId('calendar-event-zoom-link')).toBeInTheDocument();
|
||||
expect(queryByTestId('calendar-event-zoom-link')).toHaveAttribute(
|
||||
'href',
|
||||
event.conferenceData.entryPoints[0].uri,
|
||||
);
|
||||
expect(queryByTestId('calendar-event-time')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should not render time for events longer than 1 day', async () => {
|
||||
const allDayEvent = {
|
||||
summary: 'Test event',
|
||||
start: {
|
||||
date: '2022-02-09',
|
||||
},
|
||||
end: {
|
||||
date: '2022-02-19',
|
||||
},
|
||||
};
|
||||
const { queryByText, queryByTestId } = await renderInTestApp(
|
||||
<CalendarEvent event={allDayEvent} />,
|
||||
);
|
||||
expect(queryByText(allDayEvent.summary)).toBeInTheDocument();
|
||||
expect(queryByTestId('calendar-event-time')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should show popover on click', async () => {
|
||||
const { queryByTestId, getByTestId } = await renderInTestApp(
|
||||
<CalendarEvent event={event} />,
|
||||
);
|
||||
expect(queryByTestId('calendar-event-popover')).not.toBeInTheDocument();
|
||||
|
||||
fireEvent.click(getByTestId('calendar-event'));
|
||||
expect(queryByTestId('calendar-event-popover')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import classnames from 'classnames';
|
||||
import {
|
||||
bindPopover,
|
||||
bindTrigger,
|
||||
usePopupState,
|
||||
} from 'material-ui-popup-state/hooks';
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import { useAnalytics } from '@backstage/core-plugin-api';
|
||||
|
||||
import {
|
||||
Box,
|
||||
Link,
|
||||
Paper,
|
||||
Popover,
|
||||
Tooltip,
|
||||
Typography,
|
||||
makeStyles,
|
||||
} from '@material-ui/core';
|
||||
|
||||
import zoomIcon from '../../icons/zoomIcon.svg';
|
||||
import { CalendarEventPopoverContent } from './CalendarEventPopoverContent';
|
||||
import { GCalendarEvent, ResponseStatus } from '../../api';
|
||||
import { getTimePeriod, getZoomLink, isAllDay, isPassed } from './util';
|
||||
|
||||
const useStyles = makeStyles(
|
||||
theme => ({
|
||||
event: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
marginBottom: theme.spacing(1),
|
||||
cursor: 'pointer',
|
||||
paddingRight: 12,
|
||||
},
|
||||
declined: {
|
||||
textDecoration: 'line-through',
|
||||
},
|
||||
passed: {
|
||||
opacity: 0.6,
|
||||
transition: 'opacity 0.15s ease-in-out',
|
||||
'&:hover': {
|
||||
opacity: 1,
|
||||
},
|
||||
},
|
||||
link: {
|
||||
width: 48,
|
||||
height: 48,
|
||||
display: 'inline-block',
|
||||
padding: 8,
|
||||
borderRadius: '50%',
|
||||
'&:hover': {
|
||||
backgroundColor: theme.palette.grey[100],
|
||||
},
|
||||
},
|
||||
calendarColor: ({ event }: { event: GCalendarEvent }) => ({
|
||||
width: 8,
|
||||
borderTopLeftRadius: 4,
|
||||
borderBottomLeftRadius: 4,
|
||||
backgroundColor: event.primary
|
||||
? theme.palette.primary.light
|
||||
: event.backgroundColor,
|
||||
}),
|
||||
}),
|
||||
{
|
||||
name: 'GCalendarEvent',
|
||||
},
|
||||
);
|
||||
|
||||
export const CalendarEvent = ({ event }: { event: GCalendarEvent }) => {
|
||||
const classes = useStyles({ event });
|
||||
const popoverState = usePopupState({
|
||||
variant: 'popover',
|
||||
popupId: event.id,
|
||||
disableAutoFocus: true,
|
||||
});
|
||||
const [hovered, setHovered] = useState(false);
|
||||
const analytics = useAnalytics();
|
||||
const zoomLink = getZoomLink(event);
|
||||
|
||||
const { onClick, ...restBindProps } = bindTrigger(popoverState);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Paper
|
||||
onClick={e => {
|
||||
onClick(e);
|
||||
analytics.captureEvent('click', 'event info');
|
||||
}}
|
||||
{...restBindProps}
|
||||
onMouseEnter={() => setHovered(true)}
|
||||
onMouseLeave={() => setHovered(false)}
|
||||
elevation={hovered ? 4 : 1}
|
||||
className={classnames(classes.event, {
|
||||
[classes.passed]: isPassed(event),
|
||||
})}
|
||||
data-testid="calendar-event"
|
||||
>
|
||||
<Box className={classes.calendarColor} mr={1} alignSelf="stretch" />
|
||||
<Box flex={1} pt={1} pb={1}>
|
||||
<Typography
|
||||
variant="subtitle2"
|
||||
className={classnames({
|
||||
[classes.declined]:
|
||||
event.responseStatus === ResponseStatus.declined,
|
||||
})}
|
||||
>
|
||||
{event.summary}
|
||||
</Typography>
|
||||
{!isAllDay(event) && (
|
||||
<Typography variant="body2" data-testid="calendar-event-time">
|
||||
{getTimePeriod(event)}
|
||||
</Typography>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
{zoomLink && (
|
||||
<Tooltip title="Join Zoom Meeting">
|
||||
<Link
|
||||
data-testid="calendar-event-zoom-link"
|
||||
className={classes.link}
|
||||
href={zoomLink}
|
||||
target="_blank"
|
||||
onClick={e => {
|
||||
e.stopPropagation();
|
||||
analytics.captureEvent('click', 'zoom link');
|
||||
}}
|
||||
>
|
||||
<img src={zoomIcon} alt="Zoom link" />
|
||||
</Link>
|
||||
</Tooltip>
|
||||
)}
|
||||
</Paper>
|
||||
|
||||
<Popover
|
||||
{...bindPopover(popoverState)}
|
||||
anchorOrigin={{
|
||||
vertical: 'top',
|
||||
horizontal: 'left',
|
||||
}}
|
||||
transformOrigin={{
|
||||
vertical: 'top',
|
||||
horizontal: 'center',
|
||||
}}
|
||||
data-testid="calendar-event-popover"
|
||||
>
|
||||
<CalendarEventPopoverContent event={event} />
|
||||
</Popover>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
|
||||
import { CalendarEventPopoverContent } from './CalendarEventPopoverContent';
|
||||
|
||||
describe('<CalendarEventPopoverContent />', () => {
|
||||
const event = {
|
||||
summary: 'Test event',
|
||||
htmlLink: '/calendar-link',
|
||||
conferenceData: {
|
||||
entryPoints: [{ entryPointType: 'video', uri: '/zoom-link' }],
|
||||
},
|
||||
description: 'Test description',
|
||||
attendees: [
|
||||
{
|
||||
email: 'test@test.com',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
it('should render event info', async () => {
|
||||
const { queryByText, queryByTestId } = await renderInTestApp(
|
||||
<CalendarEventPopoverContent event={event} />,
|
||||
);
|
||||
expect(queryByText(event.summary)).toBeInTheDocument();
|
||||
expect(queryByText(event.description)).toBeInTheDocument();
|
||||
expect(queryByText(event.attendees[0].email)).toBeInTheDocument();
|
||||
expect(queryByText('Join Zoom Meeting')).toBeInTheDocument();
|
||||
expect(queryByText('Join Zoom Meeting')?.closest('a')).toHaveAttribute(
|
||||
'href',
|
||||
event.conferenceData.entryPoints[0].uri,
|
||||
);
|
||||
expect(queryByTestId('open-calendar-link')).toHaveAttribute(
|
||||
'href',
|
||||
event.htmlLink,
|
||||
);
|
||||
expect(queryByText(event.attendees[0].email)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { sortBy } from 'lodash';
|
||||
import React from 'react';
|
||||
import DOMPurify from 'dompurify';
|
||||
|
||||
import { useAnalytics } from '@backstage/core-plugin-api';
|
||||
|
||||
import {
|
||||
Box,
|
||||
Divider,
|
||||
IconButton,
|
||||
Link,
|
||||
Tooltip,
|
||||
Typography,
|
||||
makeStyles,
|
||||
} from '@material-ui/core';
|
||||
import ArrowForwardIcon from '@material-ui/icons/ArrowForward';
|
||||
|
||||
import { AttendeeChip } from './AttendeeChip';
|
||||
import { GCalendarEvent } from '../../api';
|
||||
import { getTimePeriod, getZoomLink } from './util';
|
||||
|
||||
const useStyles = makeStyles(
|
||||
theme => {
|
||||
return {
|
||||
description: {
|
||||
wordBreak: 'break-word',
|
||||
'& a': {
|
||||
color: theme.palette.primary.main,
|
||||
fontWeight: 500,
|
||||
},
|
||||
},
|
||||
divider: {
|
||||
marginTop: theme.spacing(2),
|
||||
marginBottom: theme.spacing(2),
|
||||
},
|
||||
};
|
||||
},
|
||||
{
|
||||
name: 'GCalendarEventPopoverContent',
|
||||
},
|
||||
);
|
||||
|
||||
type CalendarEventPopoverProps = {
|
||||
event: GCalendarEvent;
|
||||
};
|
||||
|
||||
export const CalendarEventPopoverContent = ({
|
||||
event,
|
||||
}: CalendarEventPopoverProps) => {
|
||||
const classes = useStyles({ event });
|
||||
const analytics = useAnalytics();
|
||||
const zoomLink = getZoomLink(event);
|
||||
|
||||
return (
|
||||
<Box display="flex" flexDirection="column" width={400} p={2}>
|
||||
<Box display="flex" alignItems="center">
|
||||
<Box flex={1}>
|
||||
<Typography variant="h6">{event.summary}</Typography>
|
||||
<Typography variant="subtitle2">{getTimePeriod(event)}</Typography>
|
||||
</Box>
|
||||
{event.htmlLink && (
|
||||
<Tooltip title="Open in Calendar">
|
||||
<Link
|
||||
data-testid="open-calendar-link"
|
||||
href={event.htmlLink}
|
||||
target="_blank"
|
||||
onClick={_e =>
|
||||
analytics.captureEvent('click', 'open in calendar')
|
||||
}
|
||||
>
|
||||
<IconButton>
|
||||
<ArrowForwardIcon />
|
||||
</IconButton>
|
||||
</Link>
|
||||
</Tooltip>
|
||||
)}
|
||||
</Box>
|
||||
{zoomLink && (
|
||||
<Link
|
||||
href={zoomLink}
|
||||
target="_blank"
|
||||
onClick={_e => analytics.captureEvent('click', 'zoom link')}
|
||||
>
|
||||
Join Zoom Meeting
|
||||
</Link>
|
||||
)}
|
||||
|
||||
{event.description && (
|
||||
<>
|
||||
<Divider className={classes.divider} variant="fullWidth" />
|
||||
<Box
|
||||
className={classes.description}
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: DOMPurify.sanitize(event.description, {
|
||||
USE_PROFILES: { html: true },
|
||||
}),
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
{event.attendees && (
|
||||
<>
|
||||
<Divider className={classes.divider} variant="fullWidth" />
|
||||
<Box>
|
||||
<Typography variant="subtitle2">Attendees</Typography>
|
||||
<Box mb={1} />
|
||||
{sortBy(event.attendees || [], 'email').map(user => (
|
||||
<AttendeeChip key={user.email} user={user} />
|
||||
))}
|
||||
</Box>
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { sortBy } from 'lodash';
|
||||
import React from 'react';
|
||||
|
||||
import {
|
||||
Checkbox,
|
||||
FormControl,
|
||||
Input,
|
||||
ListItemText,
|
||||
MenuItem,
|
||||
Select,
|
||||
Typography,
|
||||
makeStyles,
|
||||
} from '@material-ui/core';
|
||||
|
||||
import { GCalendar } from '../../api';
|
||||
|
||||
const useStyles = makeStyles(
|
||||
{
|
||||
formControl: {
|
||||
width: 120,
|
||||
},
|
||||
selectedCalendars: {
|
||||
textOverflow: 'ellipsis',
|
||||
overflow: 'hidden',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'GCalendarSelect',
|
||||
},
|
||||
);
|
||||
|
||||
type CalendarSelectProps = {
|
||||
disabled: boolean;
|
||||
selectedCalendars?: string[];
|
||||
setSelectedCalendars: (value: string[]) => void;
|
||||
calendars: GCalendar[];
|
||||
};
|
||||
|
||||
export const CalendarSelect = ({
|
||||
disabled,
|
||||
selectedCalendars = [],
|
||||
setSelectedCalendars,
|
||||
calendars,
|
||||
}: CalendarSelectProps) => {
|
||||
const classes = useStyles();
|
||||
|
||||
return (
|
||||
<FormControl className={classes.formControl}>
|
||||
<Select
|
||||
labelId="calendars-label"
|
||||
disabled={disabled || calendars.length === 0}
|
||||
multiple
|
||||
value={selectedCalendars}
|
||||
onChange={async e => setSelectedCalendars(e.target.value as string[])}
|
||||
input={<Input />}
|
||||
renderValue={selected => (
|
||||
<Typography className={classes.selectedCalendars} variant="body2">
|
||||
{calendars
|
||||
.filter(c => c.id && (selected as string[]).includes(c.id))
|
||||
.map(c => c.summary)
|
||||
.join(', ')}
|
||||
</Typography>
|
||||
)}
|
||||
MenuProps={{
|
||||
PaperProps: {
|
||||
style: {
|
||||
width: 350,
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
{sortBy(calendars, 'summary').map(c => (
|
||||
<MenuItem key={c.id} value={c.id}>
|
||||
<Checkbox checked={selectedCalendars.includes(c.id!)} />
|
||||
<ListItemText primary={c.summary} />
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
|
||||
import { googleAuthApiRef, storageApiRef } from '@backstage/core-plugin-api';
|
||||
import {
|
||||
MockStorageApi,
|
||||
TestApiProvider,
|
||||
renderInTestApp,
|
||||
} from '@backstage/test-utils';
|
||||
|
||||
import { HomePageCalendar } from '.';
|
||||
import { gcalendarApiRef, gcalendarPlugin } from '../..';
|
||||
|
||||
describe('<HomePageCalendar />', () => {
|
||||
const primaryCalendar = {
|
||||
id: 'test-1@test.com',
|
||||
summary: 'test-1@test.com',
|
||||
primary: true,
|
||||
};
|
||||
const nonPrimaryCalendar = {
|
||||
id: 'test-2@test.com',
|
||||
summary: 'test-2@test.com',
|
||||
primary: false,
|
||||
};
|
||||
const calendarData = {
|
||||
items: [primaryCalendar, nonPrimaryCalendar],
|
||||
};
|
||||
const eventData = {
|
||||
items: [
|
||||
{
|
||||
id: '1',
|
||||
summary: 'Test event',
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
summary: 'Test event',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const getMockedApi = (calendarMock = {}, eventMock = {}) => ({
|
||||
getCalendars: jest.fn().mockResolvedValue(calendarMock),
|
||||
getEvents: jest.fn().mockResolvedValue(eventMock),
|
||||
});
|
||||
|
||||
const getAuthMockApi = (token = 'token') => ({
|
||||
getAccessToken: jest.fn().mockResolvedValue(token),
|
||||
});
|
||||
|
||||
const mockStorage = MockStorageApi.create();
|
||||
|
||||
it('should render "Sign in" button if user not signed in', async () => {
|
||||
const rendered = await renderInTestApp(
|
||||
<TestApiProvider
|
||||
apis={[
|
||||
[gcalendarApiRef, getMockedApi()],
|
||||
[googleAuthApiRef, getAuthMockApi('')],
|
||||
]}
|
||||
>
|
||||
<HomePageCalendar />
|
||||
</TestApiProvider>,
|
||||
);
|
||||
|
||||
expect(rendered.queryByText('Sign in')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render empty card', async () => {
|
||||
const rendered = await renderInTestApp(
|
||||
<TestApiProvider
|
||||
apis={[
|
||||
[gcalendarApiRef, getMockedApi()],
|
||||
[googleAuthApiRef, getAuthMockApi()],
|
||||
]}
|
||||
>
|
||||
<HomePageCalendar />
|
||||
</TestApiProvider>,
|
||||
);
|
||||
|
||||
expect(rendered.queryByText('No events')).toBeInTheDocument();
|
||||
expect(rendered.queryByText('Go to Calendar')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should select primary calendar by default', async () => {
|
||||
const rendered = await renderInTestApp(
|
||||
<TestApiProvider
|
||||
apis={[
|
||||
[gcalendarApiRef, getMockedApi(calendarData, eventData)],
|
||||
[googleAuthApiRef, getAuthMockApi()],
|
||||
]}
|
||||
>
|
||||
<HomePageCalendar />
|
||||
</TestApiProvider>,
|
||||
);
|
||||
|
||||
expect(rendered.queryByText(primaryCalendar.summary)).toBeInTheDocument();
|
||||
expect(
|
||||
rendered.queryByText(nonPrimaryCalendar.summary),
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render calendar events', async () => {
|
||||
const rendered = await renderInTestApp(
|
||||
<TestApiProvider
|
||||
apis={[
|
||||
[gcalendarApiRef, getMockedApi(calendarData, eventData)],
|
||||
[googleAuthApiRef, getAuthMockApi()],
|
||||
]}
|
||||
>
|
||||
<HomePageCalendar />
|
||||
</TestApiProvider>,
|
||||
);
|
||||
|
||||
expect(rendered.queryByText('No events')).not.toBeInTheDocument();
|
||||
expect(rendered.queryAllByText('Test event')).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('should select stored calendar', async () => {
|
||||
mockStorage
|
||||
.forBucket(gcalendarPlugin.getId())
|
||||
.set('google_calendars_selected', [nonPrimaryCalendar.id]);
|
||||
|
||||
const rendered = await renderInTestApp(
|
||||
<TestApiProvider
|
||||
apis={[
|
||||
[gcalendarApiRef, getMockedApi(calendarData, eventData)],
|
||||
[googleAuthApiRef, getAuthMockApi()],
|
||||
[storageApiRef, mockStorage],
|
||||
]}
|
||||
>
|
||||
<HomePageCalendar />
|
||||
</TestApiProvider>,
|
||||
);
|
||||
|
||||
expect(
|
||||
rendered.queryByText(nonPrimaryCalendar.summary),
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
rendered.queryByText(primaryCalendar.summary),
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { QueryClient, QueryClientProvider } from 'react-query';
|
||||
|
||||
import { CalendarCard } from './CalendarCard';
|
||||
|
||||
const queryClient = new QueryClient();
|
||||
|
||||
export const HomePageCalendar = () => {
|
||||
return (
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<CalendarCard />
|
||||
</QueryClientProvider>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
|
||||
import { Box, Button, styled } from '@material-ui/core';
|
||||
|
||||
import { CalendarEvent } from './CalendarEvent';
|
||||
import { eventsMock } from './signInEventMock';
|
||||
import { GCalendarEvent } from '../..';
|
||||
|
||||
type Props = {
|
||||
handleAuthClick: React.MouseEventHandler<HTMLElement>;
|
||||
events?: GCalendarEvent[];
|
||||
};
|
||||
|
||||
const TransparentBox = styled(Box)({
|
||||
opacity: 0.3,
|
||||
filter: 'blur(1.5px)',
|
||||
});
|
||||
|
||||
export const SignInContent = ({
|
||||
handleAuthClick,
|
||||
events = eventsMock,
|
||||
}: Props) => {
|
||||
return (
|
||||
<Box position="relative" height="100%" width="100%">
|
||||
<TransparentBox p={1}>
|
||||
{events.map(event => (
|
||||
<CalendarEvent key={event.id} event={event} />
|
||||
))}
|
||||
</TransparentBox>
|
||||
|
||||
<Box
|
||||
height="100%"
|
||||
width="100%"
|
||||
display="flex"
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
position="absolute"
|
||||
left={0}
|
||||
top={0}
|
||||
>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={handleAuthClick}
|
||||
size="large"
|
||||
>
|
||||
Sign in
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
export { HomePageCalendar } from './HomePageCalendar';
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { GCalendarEvent } from '../../api';
|
||||
|
||||
export const eventsMock: GCalendarEvent[] = [
|
||||
{
|
||||
id: '1',
|
||||
htmlLink: 'https://www.google.com/calendar/',
|
||||
summary: 'Backstage Community Sessions',
|
||||
start: {
|
||||
dateTime: '2021-12-07T09:00:00+01:00',
|
||||
timeZone: 'Europe/London',
|
||||
},
|
||||
end: {
|
||||
dateTime: '2021-12-07T10:00:00+01:00',
|
||||
timeZone: 'Europe/London',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
htmlLink: 'https://www.google.com/calendar/',
|
||||
summary: 'Backstage Community Sessions',
|
||||
start: {
|
||||
dateTime: '2021-12-07T10:30:00+01:00',
|
||||
timeZone: 'Europe/London',
|
||||
},
|
||||
end: {
|
||||
dateTime: '2021-12-07T10:45:00+01:00',
|
||||
timeZone: 'Europe/London',
|
||||
},
|
||||
conferenceData: {
|
||||
entryPoints: [
|
||||
{
|
||||
entryPointType: 'video',
|
||||
uri: 'https://zoom.us',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
htmlLink: 'https://www.google.com/calendar',
|
||||
summary: 'Backstage Community Sessions',
|
||||
start: {
|
||||
dateTime: '2021-12-07T12:00:00+01:00',
|
||||
timeZone: 'Europe/London',
|
||||
},
|
||||
end: {
|
||||
dateTime: '2021-12-07T13:00:00+01:00',
|
||||
timeZone: 'Europe/London',
|
||||
},
|
||||
conferenceData: {
|
||||
entryPoints: [
|
||||
{
|
||||
entryPointType: 'video',
|
||||
uri: 'https://zoom.us',
|
||||
label: 'zoom.us',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
htmlLink: 'https://www.google.com/calendar',
|
||||
summary: 'Backstage Community Sessions',
|
||||
start: {
|
||||
dateTime: '2021-12-07T15:00:00+01:00',
|
||||
timeZone: 'Europe/London',
|
||||
},
|
||||
end: {
|
||||
dateTime: '2021-12-07T16:30:00+01:00',
|
||||
timeZone: 'Europe/London',
|
||||
},
|
||||
conferenceData: {
|
||||
entryPoints: [
|
||||
{
|
||||
entryPointType: 'video',
|
||||
uri: 'https://zoom.us',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: '5',
|
||||
htmlLink: 'https://www.google.com/calendar',
|
||||
summary: 'Backstage Community Sessions',
|
||||
start: {
|
||||
dateTime: '2021-12-07T17:00:00+01:00',
|
||||
timeZone: 'Europe/London',
|
||||
},
|
||||
end: {
|
||||
dateTime: '2021-12-07T17:30:00+01:00',
|
||||
timeZone: 'Europe/London',
|
||||
},
|
||||
conferenceData: {
|
||||
entryPoints: [
|
||||
{
|
||||
entryPointType: 'video',
|
||||
uri: 'https://zoom.us',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { DateTime } from 'luxon';
|
||||
|
||||
import { GCalendarEvent } from '../../api';
|
||||
|
||||
export function getZoomLink(event: GCalendarEvent) {
|
||||
const videoEntrypoint = event.conferenceData?.entryPoints?.find(
|
||||
e => e.entryPointType === 'video',
|
||||
);
|
||||
|
||||
return videoEntrypoint?.uri ?? '';
|
||||
}
|
||||
|
||||
export function getTimePeriod(event: GCalendarEvent) {
|
||||
if (isAllDay(event)) {
|
||||
return getAllDayTimePeriod(event);
|
||||
}
|
||||
|
||||
const format: Intl.DateTimeFormatOptions = {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
};
|
||||
|
||||
const startTime = DateTime.fromISO(event.start?.dateTime || '');
|
||||
const endTime = DateTime.fromISO(event.end?.dateTime || '');
|
||||
|
||||
return `${startTime.toLocaleString(format)} - ${endTime.toLocaleString(
|
||||
format,
|
||||
)}`;
|
||||
}
|
||||
|
||||
function getAllDayTimePeriod(event: GCalendarEvent) {
|
||||
const format: Intl.DateTimeFormatOptions = { month: 'long', day: 'numeric' };
|
||||
const startTime = DateTime.fromISO(
|
||||
event.start?.dateTime || event.start?.date || '',
|
||||
);
|
||||
const endTime = DateTime.fromISO(
|
||||
event.end?.dateTime || event.end?.date || '',
|
||||
).minus({ day: 1 });
|
||||
|
||||
if (startTime.toISO() === endTime.toISO()) {
|
||||
return startTime.toLocaleString(format);
|
||||
}
|
||||
|
||||
return `${startTime.toLocaleString(format)} - ${endTime.toLocaleString(
|
||||
format,
|
||||
)}`;
|
||||
}
|
||||
|
||||
export function isPassed(event: GCalendarEvent) {
|
||||
if (!event.end?.dateTime && !event.end?.date) return false;
|
||||
const eventDate = DateTime.fromISO(event.end?.dateTime || event.end?.date!);
|
||||
return DateTime.now() >= eventDate;
|
||||
}
|
||||
|
||||
export function isAllDay(event: GCalendarEvent) {
|
||||
if (event.start?.date || event.end?.date) {
|
||||
return true;
|
||||
}
|
||||
const startTime = DateTime.fromISO(event.start?.dateTime || '');
|
||||
const endTime = DateTime.fromISO(event.end?.dateTime || '');
|
||||
|
||||
return endTime.diff(startTime, 'day').days >= 1;
|
||||
}
|
||||
|
||||
export function getStartDate(event: GCalendarEvent) {
|
||||
return DateTime.fromISO(event.start?.dateTime || event.start?.date || '');
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
export { useCalendarsQuery } from './useCalendarsQuery';
|
||||
export { useEventsQuery } from './useEventsQuery';
|
||||
export { useSignIn } from './useSignIn';
|
||||
export { useStoredCalendars } from './useStoredCalendars';
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { useQuery } from 'react-query';
|
||||
|
||||
import { errorApiRef, useApi } from '@backstage/core-plugin-api';
|
||||
|
||||
import { gcalendarApiRef } from '../api';
|
||||
|
||||
type Options = {
|
||||
enabled: boolean;
|
||||
refreshTime?: number;
|
||||
};
|
||||
|
||||
export const useCalendarsQuery = ({ enabled }: Options) => {
|
||||
const calendarApi = useApi(gcalendarApiRef);
|
||||
const errorApi = useApi(errorApiRef);
|
||||
|
||||
return useQuery(
|
||||
['calendars'],
|
||||
async () =>
|
||||
calendarApi.getCalendars({
|
||||
minAccessRole: 'reader',
|
||||
}),
|
||||
{
|
||||
enabled,
|
||||
keepPreviousData: true,
|
||||
refetchInterval: 3600000,
|
||||
onError: () => {
|
||||
errorApi.post({
|
||||
name: 'API error',
|
||||
message: 'Failed to fetch calendars.',
|
||||
});
|
||||
},
|
||||
},
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { compact, unescape } from 'lodash';
|
||||
import { useMemo } from 'react';
|
||||
import { useQueries } from 'react-query';
|
||||
|
||||
import { useApi } from '@backstage/core-plugin-api';
|
||||
|
||||
import { gcalendarApiRef } from '../api';
|
||||
import { GCalendar, GCalendarEvent } from '../api';
|
||||
|
||||
type Options = {
|
||||
selectedCalendars?: string[];
|
||||
timeMin: string;
|
||||
timeMax: string;
|
||||
enabled: boolean;
|
||||
calendars: GCalendar[];
|
||||
timeZone: string;
|
||||
refreshTime?: number;
|
||||
};
|
||||
|
||||
export const useEventsQuery = ({
|
||||
selectedCalendars = [],
|
||||
calendars = [],
|
||||
enabled,
|
||||
timeMin,
|
||||
timeMax,
|
||||
timeZone,
|
||||
}: Options) => {
|
||||
const calendarApi = useApi(gcalendarApiRef);
|
||||
const eventQueries = useQueries(
|
||||
selectedCalendars
|
||||
.filter(id => calendars.find(c => c.id === id))
|
||||
.map(calendarId => {
|
||||
const calendar = calendars.find(c => c.id === calendarId);
|
||||
|
||||
return {
|
||||
queryKey: ['calendarEvents', calendarId, timeMin, timeMax],
|
||||
enabled,
|
||||
initialData: [],
|
||||
refetchInterval: 60000,
|
||||
refetchIntervalInBackground: true,
|
||||
queryFn: async (): Promise<GCalendarEvent[]> => {
|
||||
const data = await calendarApi.getEvents(calendarId, {
|
||||
calendarId,
|
||||
timeMin,
|
||||
timeMax,
|
||||
showDeleted: false,
|
||||
singleEvents: true,
|
||||
maxResults: 100,
|
||||
orderBy: 'startTime',
|
||||
timeZone,
|
||||
});
|
||||
|
||||
return (data.items || []).map(event => {
|
||||
const responseStatus = event.attendees?.find(
|
||||
a => !!a.self,
|
||||
)?.responseStatus;
|
||||
|
||||
return {
|
||||
...event,
|
||||
summary: unescape(event.summary || ''),
|
||||
calendarId,
|
||||
backgroundColor: calendar?.backgroundColor,
|
||||
primary: !!calendar?.primary,
|
||||
responseStatus,
|
||||
};
|
||||
});
|
||||
},
|
||||
};
|
||||
}),
|
||||
);
|
||||
|
||||
const events = useMemo(
|
||||
() => compact(eventQueries.map(({ data }) => data).flat()),
|
||||
[eventQueries],
|
||||
);
|
||||
|
||||
const isLoading =
|
||||
!!eventQueries.find(q => q.isFetching) && events.length === 0;
|
||||
|
||||
return { events, isLoading };
|
||||
};
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { useCallback, useState } from 'react';
|
||||
|
||||
import { googleAuthApiRef, useApi } from '@backstage/core-plugin-api';
|
||||
|
||||
export const useSignIn = () => {
|
||||
const [isSignedIn, setSignedIn] = useState(false);
|
||||
const [isInitialized, setInitialized] = useState(false);
|
||||
const authApi = useApi(googleAuthApiRef);
|
||||
|
||||
const signIn = useCallback(
|
||||
async (optional = false) => {
|
||||
const token = await authApi.getAccessToken(
|
||||
'https://www.googleapis.com/auth/calendar.readonly',
|
||||
{
|
||||
optional,
|
||||
instantPopup: !optional,
|
||||
},
|
||||
);
|
||||
|
||||
setSignedIn(!!token);
|
||||
setInitialized(true);
|
||||
},
|
||||
[authApi, setSignedIn],
|
||||
);
|
||||
|
||||
return { isSignedIn, isInitialized, signIn };
|
||||
};
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { useApi, storageApiRef } from '@backstage/core-plugin-api';
|
||||
import useObservable from 'react-use/lib/useObservable';
|
||||
|
||||
import { gcalendarPlugin } from '../plugin';
|
||||
|
||||
export enum LocalStorageKeys {
|
||||
selectedCalendars = 'google_calendars_selected',
|
||||
}
|
||||
|
||||
export function useStoredCalendars(
|
||||
defaultValue: string[],
|
||||
): [string[], (value: string[]) => void] {
|
||||
const storageBucket = gcalendarPlugin.getId();
|
||||
const storageKey = LocalStorageKeys.selectedCalendars;
|
||||
const storageApi = useApi(storageApiRef).forBucket(storageBucket);
|
||||
const setValue = (value: string[]) => {
|
||||
storageApi.set(storageKey, value);
|
||||
};
|
||||
const snapshot = useObservable(
|
||||
storageApi.observe$<string[]>(storageKey),
|
||||
storageApi.snapshot(storageKey),
|
||||
);
|
||||
let result: string[];
|
||||
if (snapshot.presence === 'absent') {
|
||||
result = defaultValue;
|
||||
} else {
|
||||
result = snapshot.value!;
|
||||
}
|
||||
return [result, setValue];
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="186 38 76 76">
|
||||
<path fill="#fff" d="M244 56h-40v40h40V56z" />
|
||||
<path fill="#EA4335" d="M244 114l18-18h-18v18z" />
|
||||
<path fill="#FBBC04" d="M262 56h-18v40h18V56z" />
|
||||
<path fill="#34A853" d="M244 96h-40v18h40V96z" />
|
||||
<path fill="#188038" d="M186 96v12c0 3.315 2.685 6 6 6h12V96h-18z" />
|
||||
<path fill="#1967D2" d="M262 56V44c0-3.315-2.685-6-6-6h-12v18h18z" />
|
||||
<path fill="#4285F4" d="M244 38h-52c-3.315 0 -6 2.685-6 6v52h18V56h40V38z" />
|
||||
<path fill="#4285F4" d="M212.205 87.03c-1.495-1.01-2.53-2.485-3.095-4.435l3.47-1.43c.315 1.2.865 2.13 1.65 2.79.78.66 1.73.985 2.84.985 1.135 0 2.11-.345 2.925-1.035s1.225-1.57 1.225-2.635c0-1.09-.43-1.98-1.29-2.67-.86-.69-1.94-1.035-3.23-1.035h-2.005V74.13h1.8c1.11 0 2.045-.3 2.805-.9.76-.6 1.14-1.42 1.14-2.465 0 -.93-.34-1.67-1.02-2.225-.68-.555-1.54-.835-2.585-.835-1.02 0 -1.83.27-2.43.815a4.784 4.784 0 0 0 -1.31 2.005l-3.435-1.43c.455-1.29 1.29-2.43 2.515-3.415 1.225-.985 2.79-1.48 4.69-1.48 1.405 0 2.67.27 3.79.815 1.12.545 2 1.3 2.635 2.26.635.965.95 2.045.95 3.245 0 1.225-.295 2.26-.885 3.11-.59.85-1.315 1.5-2.175 1.955v.205a6.605 6.605 0 0 1 2.79 2.175c.725.975 1.09 2.14 1.09 3.5 0 1.36-.345 2.575-1.035 3.64s-1.645 1.905-2.855 2.515c-1.215.61-2.58.92-4.095.92-1.755.005-3.375-.5-4.87-1.51zM233.52 69.81l-3.81 2.755-1.905-2.89 6.835-4.93h2.62V88h-3.74V69.81z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48">
|
||||
<circle cx="24" cy="24" r="20" fill="#2196f3" />
|
||||
<path fill="#fff" d="M29,31H14c-1.657,0-3-1.343-3-3V17h15c1.657,0,3,1.343,3,3V31z" />
|
||||
<polygon fill="#fff" points="37,31 31,27 31,21 37,17" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 265 B |
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
export { gcalendarPlugin, HomePageCalendar } from './plugin';
|
||||
export * from './api';
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { gcalendarPlugin } from './plugin';
|
||||
|
||||
describe('gcalendar', () => {
|
||||
it('should export plugin', () => {
|
||||
expect(gcalendarPlugin).toBeDefined();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import {
|
||||
createApiFactory,
|
||||
createComponentExtension,
|
||||
createPlugin,
|
||||
fetchApiRef,
|
||||
googleAuthApiRef,
|
||||
} from '@backstage/core-plugin-api';
|
||||
|
||||
import { GCalendarApiClient, gcalendarApiRef } from './api';
|
||||
import { rootRouteRef } from './routes';
|
||||
|
||||
export const gcalendarPlugin = createPlugin({
|
||||
id: 'gcalendar',
|
||||
routes: {
|
||||
root: rootRouteRef,
|
||||
},
|
||||
apis: [
|
||||
createApiFactory({
|
||||
api: gcalendarApiRef,
|
||||
deps: { authApi: googleAuthApiRef, fetchApi: fetchApiRef },
|
||||
factory(deps) {
|
||||
return new GCalendarApiClient(deps);
|
||||
},
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
export const HomePageCalendar = gcalendarPlugin.provide(
|
||||
createComponentExtension({
|
||||
name: 'HomePageCalendar',
|
||||
component: {
|
||||
lazy: () =>
|
||||
import('./components/CalendarCard').then(m => m.HomePageCalendar),
|
||||
},
|
||||
}),
|
||||
);
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { createRouteRef } from '@backstage/core-plugin-api';
|
||||
|
||||
export const rootRouteRef = createRouteRef({
|
||||
id: 'gcalendar',
|
||||
});
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import '@testing-library/jest-dom';
|
||||
import 'cross-fetch/polyfill';
|
||||
@@ -1295,7 +1295,7 @@
|
||||
core-js-pure "^3.20.2"
|
||||
regenerator-runtime "^0.13.4"
|
||||
|
||||
"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.0", "@babel/runtime@^7.10.1", "@babel/runtime@^7.10.2", "@babel/runtime@^7.10.4", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.5", "@babel/runtime@^7.14.6", "@babel/runtime@^7.15.4", "@babel/runtime@^7.16.3", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.4", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.0", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2":
|
||||
"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.0", "@babel/runtime@^7.10.1", "@babel/runtime@^7.10.2", "@babel/runtime@^7.10.4", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.5", "@babel/runtime@^7.14.6", "@babel/runtime@^7.15.4", "@babel/runtime@^7.16.3", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.4", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.0", "@babel/runtime@^7.6.2", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2":
|
||||
version "7.17.2"
|
||||
resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.2.tgz#66f68591605e59da47523c631416b18508779941"
|
||||
integrity sha512-hzeyJyMA1YGdJTuWU0e/j4wKXrU4OMFvY2MSlaI9B7VQb0r5cxTE3EAIS2Q7Tn2RIcDkRvTA/v2JsAEhxe99uw==
|
||||
@@ -4062,6 +4062,11 @@
|
||||
resolved "https://registry.npmjs.org/@material-ui/types/-/types-5.1.0.tgz#efa1c7a0b0eaa4c7c87ac0390445f0f88b0d88f2"
|
||||
integrity sha512-7cqRjrY50b8QzRSYyhSpx4WRw2YuO0KKIGQEVk5J8uoz2BanawykgZGoWEqKm7pVIbzFDN0SpPcVV4IhOFkl8A==
|
||||
|
||||
"@material-ui/types@^6.0.1":
|
||||
version "6.0.2"
|
||||
resolved "https://registry.npmjs.org/@material-ui/types/-/types-6.0.2.tgz#a6d384196c6e2e130eb2765b62d944c0b1ea1015"
|
||||
integrity sha512-/XUca4wUb9pWimLLdM1PE8KS8rTbDEGohSGkGtk3WST7lm23m+8RYv9uOmrvOg/VSsl4bMiOv4t2/LCb+RLbTg==
|
||||
|
||||
"@material-ui/utils@^4.11.2", "@material-ui/utils@^4.7.1":
|
||||
version "4.11.2"
|
||||
resolved "https://registry.npmjs.org/@material-ui/utils/-/utils-4.11.2.tgz#f1aefa7e7dff2ebcb97d31de51aecab1bb57540a"
|
||||
@@ -4071,6 +4076,13 @@
|
||||
prop-types "^15.7.2"
|
||||
react-is "^16.8.0 || ^17.0.0"
|
||||
|
||||
"@maxim_mazurok/gapi.client.calendar@latest":
|
||||
version "3.0.20220217"
|
||||
resolved "https://registry.npmjs.org/@maxim_mazurok/gapi.client.calendar/-/gapi.client.calendar-3.0.20220217.tgz#86279915171ddc5f29afba9f9c19244d3e842a07"
|
||||
integrity sha512-FMXqpSBmws0Arxrpg3zJJdM2NwYFd66N/C9blAAw9wt68aWa74N6okLVvOCs1FJF9asF7gHBZn8jAP+DHk+yxg==
|
||||
dependencies:
|
||||
"@types/gapi.client" "*"
|
||||
|
||||
"@microsoft/api-documenter@^7.15.0":
|
||||
version "7.15.0"
|
||||
resolved "https://registry.npmjs.org/@microsoft/api-documenter/-/api-documenter-7.15.0.tgz#e6cf24fc0e2f18a71dcf4c5c8100cc083167a81e"
|
||||
@@ -5712,7 +5724,7 @@
|
||||
"@types/docker-modem" "*"
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/dompurify@^2.1.0", "@types/dompurify@^2.2.2":
|
||||
"@types/dompurify@^2.1.0", "@types/dompurify@^2.2.2", "@types/dompurify@^2.3.3":
|
||||
version "2.3.3"
|
||||
resolved "https://registry.npmjs.org/@types/dompurify/-/dompurify-2.3.3.tgz#c24c92f698f77ed9cc9d9fa7888f90cf2bfaa23f"
|
||||
integrity sha512-nnVQSgRVuZ/843oAfhA25eRSNzUFcBPk/LOiw5gm8mD9/X7CNcbRkQu/OsjCewO8+VIYfPxUnXvPEVGenw14+w==
|
||||
@@ -5806,6 +5818,30 @@
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/gapi.auth2@^0.0.56":
|
||||
version "0.0.56"
|
||||
resolved "https://registry.npmjs.org/@types/gapi.auth2/-/gapi.auth2-0.0.56.tgz#2f7031f79390b8401e7950d8277ada874fd2731c"
|
||||
integrity sha512-kGaBtGVCqGS3Y05L56dGVlBpJflxLfwA0zpMQnQgGRFk1tsMPbQnogG51UQjt1vCuYfRO0Jd9/K5KDtzjAbMkA==
|
||||
dependencies:
|
||||
"@types/gapi" "*"
|
||||
|
||||
"@types/gapi.client.calendar@^3.0.10":
|
||||
version "3.0.10"
|
||||
resolved "https://registry.npmjs.org/@types/gapi.client.calendar/-/gapi.client.calendar-3.0.10.tgz#4b089d9af2753a07cf1d46adc83b1a7cedb8e355"
|
||||
integrity sha512-NUStEVbHPOhFsw4cWE2CThe5eKpTlmz+fSu8mvEc7j+IDVNgk1kS4C6hZzBCdlIjFfOzdQM3Cyqkt5kt7ze3kA==
|
||||
dependencies:
|
||||
"@maxim_mazurok/gapi.client.calendar" latest
|
||||
|
||||
"@types/gapi.client@*":
|
||||
version "1.0.5"
|
||||
resolved "https://registry.npmjs.org/@types/gapi.client/-/gapi.client-1.0.5.tgz#a6eb97e664fe51656c5b52258bd0afef28c76308"
|
||||
integrity sha512-OTpbBMuzfC4lkvaomxqskI/iWRGW3zOZbDXZLNSyiuswTiSSGgILRLkg0POuZ4EgzEdaYaTlXpnXiCp07ri/Yw==
|
||||
|
||||
"@types/gapi@*", "@types/gapi@^0.0.41":
|
||||
version "0.0.41"
|
||||
resolved "https://registry.npmjs.org/@types/gapi/-/gapi-0.0.41.tgz#c477ee4f0951c005869219fd10b456ae2bba437e"
|
||||
integrity sha512-tmHO66z/f91JZCDqinj/nNvQEszsz/hBT4+MvCSKT5sDzl5Ld/oXZ8WaecCBjRLw2uWKUInUHM9MhEXWkOiNjw==
|
||||
|
||||
"@types/git-url-parse@^9.0.0":
|
||||
version "9.0.1"
|
||||
resolved "https://registry.npmjs.org/@types/git-url-parse/-/git-url-parse-9.0.1.tgz#1c7cc89527ca8b5afcf260ead3b0e4e373c43938"
|
||||
@@ -6431,6 +6467,13 @@
|
||||
dependencies:
|
||||
rollup-plugin-postcss "*"
|
||||
|
||||
"@types/sanitize-html@^2.6.2":
|
||||
version "2.6.2"
|
||||
resolved "https://registry.npmjs.org/@types/sanitize-html/-/sanitize-html-2.6.2.tgz#9c47960841b9def1e4c9dfebaaab010a3f6e97b9"
|
||||
integrity sha512-7Lu2zMQnmHHQGKXVvCOhSziQMpa+R2hMHFefzbYoYMHeaXR0uXqNeOc3JeQQQ8/6Xa2Br/P1IQTLzV09xxAiUQ==
|
||||
dependencies:
|
||||
htmlparser2 "^6.0.0"
|
||||
|
||||
"@types/scheduler@*":
|
||||
version "0.16.1"
|
||||
resolved "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.1.tgz#18845205e86ff0038517aab7a18a62a6b9f71275"
|
||||
@@ -8121,6 +8164,11 @@ bfj@^7.0.2:
|
||||
hoopy "^0.1.4"
|
||||
tryer "^1.0.1"
|
||||
|
||||
big-integer@^1.6.16:
|
||||
version "1.6.51"
|
||||
resolved "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz#0df92a5d9880560d3ff2d5fd20245c889d130686"
|
||||
integrity sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==
|
||||
|
||||
big-integer@^1.6.17:
|
||||
version "1.6.48"
|
||||
resolved "https://registry.npmjs.org/big-integer/-/big-integer-1.6.48.tgz#8fd88bd1632cba4a1c8c3e3d7159f08bb95b4b9e"
|
||||
@@ -8319,6 +8367,20 @@ breakword@^1.0.5:
|
||||
dependencies:
|
||||
wcwidth "^1.0.1"
|
||||
|
||||
broadcast-channel@^3.4.1:
|
||||
version "3.7.0"
|
||||
resolved "https://registry.npmjs.org/broadcast-channel/-/broadcast-channel-3.7.0.tgz#2dfa5c7b4289547ac3f6705f9c00af8723889937"
|
||||
integrity sha512-cIAKJXAxGJceNZGTZSBzMxzyOn72cVgPnKx4dc6LRjQgbaJUQqhy5rzL3zbMxkMWsGKkv2hSFkPRMEXfoMZ2Mg==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.7.2"
|
||||
detect-node "^2.1.0"
|
||||
js-sha3 "0.8.0"
|
||||
microseconds "0.2.0"
|
||||
nano-time "1.0.0"
|
||||
oblivious-set "1.0.0"
|
||||
rimraf "3.0.2"
|
||||
unload "2.2.0"
|
||||
|
||||
brorand@^1.0.1, brorand@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f"
|
||||
@@ -10784,6 +10846,11 @@ detect-node@^2.0.4:
|
||||
resolved "https://registry.npmjs.org/detect-node/-/detect-node-2.0.4.tgz#014ee8f8f669c5c58023da64b8179c083a28c46c"
|
||||
integrity sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==
|
||||
|
||||
detect-node@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1"
|
||||
integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==
|
||||
|
||||
detect-port-alt@^1.1.6:
|
||||
version "1.1.6"
|
||||
resolved "https://registry.npmjs.org/detect-port-alt/-/detect-port-alt-1.1.6.tgz#24707deabe932d4a3cf621302027c2b266568275"
|
||||
@@ -11009,7 +11076,7 @@ dompurify@=2.3.3:
|
||||
resolved "https://registry.npmjs.org/dompurify/-/dompurify-2.3.3.tgz#c1af3eb88be47324432964d8abc75cf4b98d634c"
|
||||
integrity sha512-dqnqRkPMAjOZE0FogZ+ceJNM2dZ3V/yNOuFB7+39qpO93hHhfRpHw3heYQC7DPK9FqbQTfBKUJhiSfz4MvXYwg==
|
||||
|
||||
dompurify@^2.2.7, dompurify@^2.2.9:
|
||||
dompurify@^2.2.7, dompurify@^2.2.9, dompurify@^2.3.6:
|
||||
version "2.3.6"
|
||||
resolved "https://registry.npmjs.org/dompurify/-/dompurify-2.3.6.tgz#2e019d7d7617aacac07cbbe3d88ae3ad354cf875"
|
||||
integrity sha512-OFP2u/3T1R5CEgWCEONuJ1a5+MFKnOYpkywpUSxv/dj1LeBT1erK+JwM7zK0ROy2BRhqVCf0LRw/kHqKuMkVGg==
|
||||
@@ -13837,7 +13904,7 @@ html-webpack-plugin@^5.3.1:
|
||||
pretty-error "^4.0.0"
|
||||
tapable "^2.0.0"
|
||||
|
||||
htmlparser2@^6.1.0:
|
||||
htmlparser2@^6.0.0, htmlparser2@^6.1.0:
|
||||
version "6.1.0"
|
||||
resolved "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz#c4d762b6c3371a05dbe65e94ae43a9f845fb8fb7"
|
||||
integrity sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==
|
||||
@@ -15625,6 +15692,11 @@ js-levenshtein@^1.1.6:
|
||||
resolved "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.6.tgz#c6cee58eb3550372df8deb85fad5ce66ce01d59d"
|
||||
integrity sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==
|
||||
|
||||
js-sha3@0.8.0:
|
||||
version "0.8.0"
|
||||
resolved "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840"
|
||||
integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==
|
||||
|
||||
"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
|
||||
@@ -16945,6 +17017,11 @@ luxon@^2.0.2:
|
||||
resolved "https://registry.npmjs.org/luxon/-/luxon-2.3.0.tgz#bf16a7e642513c2a20a6230a6a41b0ab446d0045"
|
||||
integrity sha512-gv6jZCV+gGIrVKhO90yrsn8qXPKD8HYZJtrUDSfEbow8Tkw84T9OnCyJhWvnJIaIF/tBuiAjZuQHUt1LddX2mg==
|
||||
|
||||
luxon@^2.3.0:
|
||||
version "2.3.1"
|
||||
resolved "https://registry.npmjs.org/luxon/-/luxon-2.3.1.tgz#f276b1b53fd9a740a60e666a541a7f6dbed4155a"
|
||||
integrity sha512-I8vnjOmhXsMSlNMZlMkSOvgrxKJl0uOsEzdGgGNZuZPaS9KlefpE9KV95QFftlJSC+1UyCC9/I69R02cz/zcCA==
|
||||
|
||||
lz-string@^1.4.4:
|
||||
version "1.4.4"
|
||||
resolved "https://registry.npmjs.org/lz-string/-/lz-string-1.4.4.tgz#c0d8eaf36059f705796e1e344811cf4c498d3a26"
|
||||
@@ -17109,6 +17186,24 @@ marked@^4.0.10:
|
||||
resolved "https://registry.npmjs.org/marked/-/marked-4.0.10.tgz#423e295385cc0c3a70fa495e0df68b007b879423"
|
||||
integrity sha512-+QvuFj0nGgO970fySghXGmuw+Fd0gD2x3+MqCWLIPf5oxdv1Ka6b2q+z9RP01P/IaKPMEramy+7cNy/Lw8c3hw==
|
||||
|
||||
match-sorter@^6.0.2:
|
||||
version "6.3.1"
|
||||
resolved "https://registry.npmjs.org/match-sorter/-/match-sorter-6.3.1.tgz#98cc37fda756093424ddf3cbc62bfe9c75b92bda"
|
||||
integrity sha512-mxybbo3pPNuA+ZuCUhm5bwNkXrJTbsk5VWbR5wiwz/GC6LIiegBGn2w3O08UG/jdbYLinw51fSQ5xNU1U3MgBw==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.12.5"
|
||||
remove-accents "0.4.2"
|
||||
|
||||
material-ui-popup-state@^1.9.3:
|
||||
version "1.9.3"
|
||||
resolved "https://registry.npmjs.org/material-ui-popup-state/-/material-ui-popup-state-1.9.3.tgz#133ee02be8adf936e738d6b5f3dc89726af39bce"
|
||||
integrity sha512-+Ete5Tzw5rXlYfmqptOS8kBUH8vnK5OJsd6IQ7SHtLjU0PsvsmM73M/k8ot0xkX4RmPGuNRsFbK3mlCe/ClQuw==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.12.5"
|
||||
"@material-ui/types" "^6.0.1"
|
||||
classnames "^2.2.6"
|
||||
prop-types "^15.7.2"
|
||||
|
||||
material-ui-search-bar@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.npmjs.org/material-ui-search-bar/-/material-ui-search-bar-1.0.0.tgz#2652dd5bdc4cb043cffb7144d9c296c120702e62"
|
||||
@@ -17700,6 +17795,11 @@ micromatch@^4.0.2, micromatch@^4.0.4:
|
||||
braces "^3.0.1"
|
||||
picomatch "^2.2.3"
|
||||
|
||||
microseconds@0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.npmjs.org/microseconds/-/microseconds-0.2.0.tgz#233b25f50c62a65d861f978a4a4f8ec18797dc39"
|
||||
integrity sha512-n7DHHMjR1avBbSpsTBj6fmMGh2AGrifVV4e+WYc3Q9lO+xnSZ3NyhcBND3vzzatt05LFhoKFRxrIyklmLlUtyA==
|
||||
|
||||
miller-rabin@^4.0.0:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d"
|
||||
@@ -18162,6 +18262,13 @@ nano-css@^5.3.1:
|
||||
stacktrace-js "^2.0.2"
|
||||
stylis "^4.0.6"
|
||||
|
||||
nano-time@1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.npmjs.org/nano-time/-/nano-time-1.0.0.tgz#b0554f69ad89e22d0907f7a12b0993a5d96137ef"
|
||||
integrity sha1-sFVPaa2J4i0JB/ehKwmTpdlhN+8=
|
||||
dependencies:
|
||||
big-integer "^1.6.16"
|
||||
|
||||
nanoclone@^0.2.1:
|
||||
version "0.2.1"
|
||||
resolved "https://registry.npmjs.org/nanoclone/-/nanoclone-0.2.1.tgz#dd4090f8f1a110d26bb32c49ed2f5b9235209ed4"
|
||||
@@ -18795,6 +18902,11 @@ object.values@^1.1.5:
|
||||
define-properties "^1.1.3"
|
||||
es-abstract "^1.19.1"
|
||||
|
||||
oblivious-set@1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.npmjs.org/oblivious-set/-/oblivious-set-1.0.0.tgz#c8316f2c2fb6ff7b11b6158db3234c49f733c566"
|
||||
integrity sha512-z+pI07qxo4c2CulUHCDf9lcqDlMSo72N/4rLUpRXf6fu+q8vjt8y0xS+Tlf8NTJDdTXHbdeO1n3MlbctwEoXZw==
|
||||
|
||||
obuf@^1.0.0, obuf@^1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e"
|
||||
@@ -20916,6 +21028,15 @@ react-markdown@^8.0.0:
|
||||
unist-util-visit "^4.0.0"
|
||||
vfile "^5.0.0"
|
||||
|
||||
react-query@^3.34.16:
|
||||
version "3.34.16"
|
||||
resolved "https://registry.npmjs.org/react-query/-/react-query-3.34.16.tgz#279ea180bcaeaec49c7864b29d1711ee9f152594"
|
||||
integrity sha512-7FvBvjgEM4YQ8nPfmAr+lJfbW95uyW/TVjFoi2GwCkF33/S8ajx45tuPHPFGWs4qYwPy1mzwxD4IQfpUDrefNQ==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.5.5"
|
||||
broadcast-channel "^3.4.1"
|
||||
match-sorter "^6.0.2"
|
||||
|
||||
react-redux@^7.1.1, react-redux@^7.2.4:
|
||||
version "7.2.5"
|
||||
resolved "https://registry.npmjs.org/react-redux/-/react-redux-7.2.5.tgz#213c1b05aa1187d9c940ddfc0b29450957f6a3b8"
|
||||
@@ -21659,6 +21780,11 @@ remedial@^1.0.7:
|
||||
resolved "https://registry.npmjs.org/remedial/-/remedial-1.0.8.tgz#a5e4fd52a0e4956adbaf62da63a5a46a78c578a0"
|
||||
integrity sha512-/62tYiOe6DzS5BqVsNpH/nkGlX45C/Sp6V+NtiN6JQNS1Viay7cWkazmRkrQrdFj2eshDe96SIQNIoMxqhzBOg==
|
||||
|
||||
remove-accents@0.4.2:
|
||||
version "0.4.2"
|
||||
resolved "https://registry.npmjs.org/remove-accents/-/remove-accents-0.4.2.tgz#0a43d3aaae1e80db919e07ae254b285d9e1c7bb5"
|
||||
integrity sha1-CkPTqq4egNuRngeuJUsoXZ4ce7U=
|
||||
|
||||
remove-trailing-separator@^1.0.1:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef"
|
||||
@@ -24664,6 +24790,14 @@ unixify@1.0.0, unixify@^1.0.0:
|
||||
dependencies:
|
||||
normalize-path "^2.1.1"
|
||||
|
||||
unload@2.2.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.npmjs.org/unload/-/unload-2.2.0.tgz#ccc88fdcad345faa06a92039ec0f80b488880ef7"
|
||||
integrity sha512-B60uB5TNBLtN6/LsgAf3udH9saB5p7gqJwcFfbOEZ8BcBHnGwCf6G/TGiEqkRAxX7zAFIUtzdrXQSdL3Q/wqNA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.6.2"
|
||||
detect-node "^2.0.4"
|
||||
|
||||
unpipe@1.0.0, unpipe@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
|
||||
|
||||
Reference in New Issue
Block a user