Compare commits

...

761 Commits

Author SHA1 Message Date
Alejandro Celaya
f16e9565e2 Added v3.1.1 to changelog 2021-05-08 11:04:21 +02:00
Alejandro Celaya
e65f9a7b89 Merge pull request #419 from acelaya-forks/feature/edit-feedback
Feature/edit feedback
2021-05-08 11:02:17 +02:00
Alejandro Celaya
0141a1e0ed Updated changelog 2021-05-08 10:57:12 +02:00
Alejandro Celaya
937876ce67 Improved feedback when editing a short URL 2021-05-08 10:56:20 +02:00
Alejandro Celaya
b52120e0d3 Updated changelog 2021-05-07 20:37:41 +02:00
Alejandro Celaya
62b65334b5 Merge pull request #418 from antwonw/patch-1
Update QrCodeModal.tsx
2021-05-07 20:36:25 +02:00
antwonw
76dae535d9 Update QrCodeModal.tsx
Remove indivisible class to fix hyperlink extending outside modal.
2021-05-06 16:56:53 -07:00
Alejandro Celaya
23ba140ff4 Merge pull request #416 from acelaya-forks/feature/prepend-visits
Feature/prepend visits
2021-05-01 16:44:21 +02:00
Alejandro Celaya
76ff7d81b9 Updated changelog 2021-05-01 16:40:22 +02:00
Alejandro Celaya
66deba29f5 Ensured new visits are prepended and not appended, ensuring they keep the proper order 2021-05-01 16:39:13 +02:00
Alejandro Celaya
e44527e9c9 Merge pull request #415 from acelaya-forks/feature/update-url-on-list
Feature/update url on list
2021-04-24 18:06:02 +02:00
Alejandro Celaya
aec629b95c Updated changelog 2021-04-24 18:01:41 +02:00
Alejandro Celaya
fa4664e583 Ensured edited short URLs are reflected in redux state when needed 2021-04-24 17:58:37 +02:00
Alejandro Celaya
2952ac8892 Merge pull request #406 from acelaya-forks/feature/reduce-margins
Feature/reduce margins
2021-03-29 21:26:49 +02:00
Alejandro Celaya
cf4fc4fa30 Updated changelog 2021-03-29 21:22:28 +02:00
Alejandro Celaya
2d61748aac Fixed styles in disabled days in date picker 2021-03-29 21:21:53 +02:00
Alejandro Celaya
7f61825768 Reduced and standardized overall vertical spacing 2021-03-29 21:08:48 +02:00
Alejandro Celaya
c3d6c83ec4 Merge pull request #405 from acelaya-forks/feature/orphan-visits-improvements
Feature/orphan visits improvements
2021-03-28 21:07:52 +02:00
Alejandro Celaya
c3e38fd580 Improved VisitsParser test 2021-03-28 21:03:46 +02:00
Alejandro Celaya
db778a73f7 Updated changelog 2021-03-28 20:57:19 +02:00
Alejandro Celaya
f0a04ced75 Added graph with orphan visits grouped by visited URL 2021-03-28 20:56:16 +02:00
Alejandro Celaya
d6bb718672 Added filtering by type to orphan visits 2021-03-28 17:45:47 +02:00
Alejandro Celaya
6d887ec4a8 Replaced custom reducers with ramda's countBy 2021-03-28 16:27:31 +02:00
Alejandro Celaya
859cd9e5e3 Improved VisitsTable test 2021-03-28 16:06:37 +02:00
Alejandro Celaya
eabd7d9ecb Added visited URL column on visits table for orphan visits 2021-03-28 15:57:22 +02:00
Alejandro Celaya
205e3ffb90 Merge pull request #404 from acelaya-forks/feature/edit-title
Feature/edit title
2021-03-27 19:01:02 +01:00
Alejandro Celaya
8c7a91c7b8 Memoized initial state for editing short URL, to ensure the form values are not reset while saving 2021-03-27 18:56:24 +01:00
Alejandro Celaya
56aab349db Updated ShortUrlForm to ensure it does not render empty cards 2021-03-27 18:39:55 +01:00
Alejandro Celaya
6628a4059e Updated changelog 2021-03-27 17:58:17 +01:00
Alejandro Celaya
10c9f7dabd Added header to EditShortUrl and created EditSHortUrl test 2021-03-27 17:56:46 +01:00
Alejandro Celaya
d703e5e182 Deleted reducers for short URL tags and short URL meta 2021-03-27 14:13:10 +01:00
Alejandro Celaya
3ad0c4d009 Deleted modals that were used to edit short URLs, since now there's a dedicated section 2021-03-27 10:49:23 +01:00
Alejandro Celaya
1403538660 Removed children from ShortUrlForm 2021-03-27 10:41:13 +01:00
Alejandro Celaya
ca670d810d Added error/loading handling to edit short URL 2021-03-27 10:27:46 +01:00
Alejandro Celaya
d5e20f445d Ensured title is not sent when its value is empty during short URL creation/edition 2021-03-27 10:19:35 +01:00
Alejandro Celaya
eea76d88c3 Ensured all data can be set when editing a short URL 2021-03-27 09:49:47 +01:00
Alejandro Celaya
a019bd30df Created view to edit short URLs 2021-03-20 16:32:12 +01:00
Alejandro Celaya
631b46393b Added title to short URL form 2021-03-20 11:18:00 +01:00
Alejandro Celaya
98aa85ca14 Created reusable component to have a short URL form 2021-03-19 19:11:27 +01:00
Alejandro Celaya
ea01d22369 Merge pull request #403 from acelaya-forks/feature/export-stats
Feature/export stats
2021-03-14 18:18:29 +01:00
Alejandro Celaya
ff1d2f63c8 Updated changelog 2021-03-14 18:14:10 +01:00
Alejandro Celaya
71468379bd Fixed headers when exporting visits to CSV 2021-03-14 18:12:10 +01:00
Alejandro Celaya
843f646264 Improved styling of the export visits button 2021-03-14 13:31:58 +01:00
Alejandro Celaya
508623f89f Improved styling of the export visits button 2021-03-14 13:30:50 +01:00
Alejandro Celaya
482489599e Created VisitsExporter test 2021-03-14 13:16:24 +01:00
Alejandro Celaya
03f63e3ee3 Added button to export visits as CSV 2021-03-14 12:53:01 +01:00
Alejandro Celaya
3f3523b80f Extracted helper function to generate a Csv file 2021-03-14 11:47:23 +01:00
Alejandro Celaya
1594717f33 Merge pull request #402 from acelaya-forks/feature/visits-default-value
Feature/visits default value
2021-03-06 17:38:08 +01:00
Alejandro Celaya
ed92b9c949 Updated changelog 2021-03-06 17:33:34 +01:00
Alejandro Celaya
e76b22b2ae Ensured consistent heights in settings cards 2021-03-06 17:30:21 +01:00
Alejandro Celaya
e380ddb40f Replaced test by it in tests 2021-03-06 17:25:09 +01:00
Alejandro Celaya
426d000a59 Added tests for new visits settings 2021-03-06 17:21:23 +01:00
Alejandro Celaya
fee62484b5 Created section to set default date interval for visits 2021-03-06 16:54:43 +01:00
Alejandro Celaya
d3f9650e82 Added new visits settings 2021-03-06 10:56:49 +01:00
Alejandro Celaya
ad46927750 Merge pull request #401 from acelaya-forks/feature/feature-improvements
Feature/feature improvements
2021-03-06 10:21:56 +01:00
Alejandro Celaya
bd79230007 Fixed version definition 2021-03-06 10:16:13 +01:00
Alejandro Celaya
5224e7b4ef Created new feature checkers 2021-03-06 09:58:29 +01:00
Alejandro Celaya
70ce099913 Added stricter types for SemVer versions 2021-03-06 09:38:48 +01:00
Alejandro Celaya
b4c2fb5b8f Merge pull request #400 from acelaya-forks/feature/improve-reducer
Feature/improve reducer
2021-03-05 16:31:18 +01:00
Alejandro Celaya
6fbf65c873 Updated changelog 2021-03-05 16:26:29 +01:00
Alejandro Celaya
13d3a95a06 Improved short URL detail redux action so that it avoids API calls when the URL is found in local state 2021-03-05 16:25:20 +01:00
Alejandro Celaya
56b3523c5b Moved short URL detail reducer to short-urls module 2021-03-05 16:04:02 +01:00
Alejandro Celaya
8a69adfbc9 Merge pull request #399 from acelaya-forks/feature/title-in-list
Feature/title in list
2021-03-05 15:48:59 +01:00
Alejandro Celaya
87a32b412f Added short URL title to visits header 2021-03-05 15:44:15 +01:00
Alejandro Celaya
df87ad5867 Updated changelog 2021-03-05 15:24:38 +01:00
Alejandro Celaya
f15bbcd027 Improved ShortUrlsRow test 2021-03-05 15:23:38 +01:00
Alejandro Celaya
3c9c0fe994 Added support for title field in short URL table 2021-03-05 14:20:49 +01:00
Alejandro Celaya
a665e96908 Updated to coding-standard v1.2.2 2021-03-05 11:14:58 +01:00
Alejandro Celaya
fddba80b08 Merge pull request #396 from acelaya-forks/feature/updated-deps
Feature/updated deps
2021-02-28 19:11:02 +01:00
Alejandro Celaya
caa3a09827 Fixed TS error in test 2021-02-28 19:00:11 +01:00
Alejandro Celaya
fa70520f38 Fixed props interface definition 2021-02-28 18:57:27 +01:00
Alejandro Celaya
b789f64a54 Updated changelog 2021-02-28 18:51:18 +01:00
Alejandro Celaya
ce0fc1094e Enabled @typescript-eslint/no-unsafe-return eslint rule again 2021-02-28 18:48:36 +01:00
Alejandro Celaya
ad0a889548 Enabled @typescript-eslint/no-base-to-string eslint rule again 2021-02-28 18:22:44 +01:00
Alejandro Celaya
1fe76500e8 Enabled @typescript-eslint/no-unsafe-call eslint rule again 2021-02-28 17:43:41 +01:00
Alejandro Celaya
86544f4b24 Enabled @typescript-eslint/unbound-method eslint rule again 2021-02-28 17:21:26 +01:00
Alejandro Celaya
c8f8416c06 No longer continue on error when linting fails during CI 2021-02-28 17:02:47 +01:00
Alejandro Celaya
3d2228441a Enabled @typescript-eslint/ban-ts-comment eslint rule 2021-02-28 13:11:27 +01:00
Alejandro Celaya
3f616d5482 Updated to @shlinkio/eslint-config-js-coding-standard@1.2.1 2021-02-28 13:01:11 +01:00
Alejandro Celaya
47fb26368b Updated dependencies and fixed coding styles 2021-02-28 12:56:56 +01:00
Alejandro Celaya
fb2194d2d1 Updated dependencies 2021-02-28 11:13:07 +01:00
Alejandro Celaya
8ec49b8cfc Merge pull request #394 from acelaya-forks/feature/orphan-visits-stats
Feature/orphan visits stats
2021-02-28 11:00:33 +01:00
Alejandro Celaya
4d77c3abf9 Updated changelog 2021-02-28 10:46:57 +01:00
Alejandro Celaya
d921c44d3b Created orphanVisitsReducer test 2021-02-28 10:45:14 +01:00
Alejandro Celaya
eb0ab92472 Created OrphanVisits test 2021-02-28 10:36:56 +01:00
Alejandro Celaya
9904ac757b Updated mercure integration so that the hook accepts a list of topics to subscribe 2021-02-28 10:12:30 +01:00
Alejandro Celaya
71ee886e24 Updated overview page cards to be links to other sections when suitable 2021-02-28 09:50:01 +01:00
Alejandro Celaya
25e53bf627 Created MenuLayout test 2021-02-28 09:28:46 +01:00
Alejandro Celaya
d7edd69e60 Created OrphanVisitsTitle test 2021-02-28 08:52:31 +01:00
Alejandro Celaya
115038f80f Created visits type helpers test 2021-02-27 20:13:18 +01:00
Alejandro Celaya
5479210366 Created section to display orphan visits stats 2021-02-27 20:03:51 +01:00
Alejandro Celaya
46d012b6ff Merge pull request #393 from acelaya-forks/feature/patch-tags
Feature/patch tags
2021-02-27 10:03:36 +01:00
Alejandro Celaya
80dcbf0668 Updated changelog 2021-02-27 09:51:31 +01:00
Alejandro Celaya
d0825089d0 Enhanced edit tags action so that it calls PATCH endpoint 2021-02-27 09:49:56 +01:00
Alejandro Celaya
f653739d50 Merge pull request #391 from acelaya-forks/feature/dark-theme
Feature/dark theme
2021-02-27 09:02:58 +01:00
Alejandro Celaya
2553b27d7d Rolled-back blurred modal 2021-02-27 08:52:10 +01:00
Alejandro Celaya
3cd30b61e4 More style fixes for dark theme 2021-02-27 08:34:44 +01:00
Alejandro Celaya
ae4921b865 Improved contrast in input border colors for dark theme 2021-02-26 23:10:19 +01:00
Alejandro Celaya
c89bcab770 Improved contrast in border colors for dark theme 2021-02-26 23:03:14 +01:00
Alejandro Celaya
f97ef8df83 Added proper blurred background for modals 2021-02-21 21:05:59 +01:00
Alejandro Celaya
e7466ced18 Added dark theme styles for date picker 2021-02-21 21:05:59 +01:00
Alejandro Celaya
0ee899f309 Updated changelog 2021-02-21 21:05:59 +01:00
Alejandro Celaya
36c97ad804 Updated styles in tags section to make it more dark-theme friendly 2021-02-21 21:05:30 +01:00
Alejandro Celaya
d6633f7555 More dark theme styles for visits page 2021-02-21 21:05:30 +01:00
Alejandro Celaya
61af43f9d9 Fixed visits table styles for dark theme 2021-02-21 21:05:30 +01:00
Alejandro Celaya
9523277311 Added icon to show which theme is selected 2021-02-21 21:05:30 +01:00
Alejandro Celaya
9703eba6ec Fixed styles for disabled inputs in dark theme 2021-02-21 21:05:30 +01:00
Alejandro Celaya
83791157ce Fixed inputs colors in dark theme when they are outside of cards 2021-02-21 21:05:30 +01:00
Alejandro Celaya
7f6c71e8d7 Created UserInterface test 2021-02-21 21:05:30 +01:00
Alejandro Celaya
9dbf790cc8 Added components and logic to dynamically change theme 2021-02-21 21:05:30 +01:00
Alejandro Celaya
f313a39b81 Added brand color and input styles to dark theme 2021-02-21 21:05:30 +01:00
Alejandro Celaya
53f16ac8b5 Added primary color alfa and tables color 2021-02-21 21:05:30 +01:00
Alejandro Celaya
13c681dc39 Added first bits of the dark theme styles 2021-02-21 21:05:30 +01:00
Alejandro Celaya
f35be007c1 Merge pull request #392 from acelaya-forks/feature/orphan-visits-card
Feature/orphan visits card
2021-02-21 21:04:37 +01:00
Alejandro Celaya
e2d26e8bdd Updated changelog 2021-02-21 20:57:57 +01:00
Alejandro Celaya
5a373fd7ae Added new card in overview to display orphan visits 2021-02-21 20:55:39 +01:00
Alejandro Celaya
3c53f7d0fc Merge pull request #389 from acelaya-forks/feature/validate-urls-setting
Feature/validate urls setting
2021-02-14 17:38:08 +01:00
Alejandro Celaya
57e3db1e1c Updated changelog 2021-02-14 17:34:20 +01:00
Alejandro Celaya
5afd3869dd Created ShortUrlCreation test 2021-02-14 17:33:01 +01:00
Alejandro Celaya
c3ebb0d10f Added test for setShortUrlCreationSettings action 2021-02-14 13:28:17 +01:00
Alejandro Celaya
4885088d59 Added option to customize initial state fo the 'Validate URL' option 2021-02-14 13:23:42 +01:00
Alejandro Celaya
872890e674 Merge pull request #388 from acelaya-forks/feature/qr-code-margin
Feature/qr code margin
2021-02-14 10:34:18 +01:00
Alejandro Celaya
8a2e39a935 Added subtle shadow in QR code image, so that it's easier to notice the margin 2021-02-14 10:21:10 +01:00
Alejandro Celaya
f8edcda665 Updated changelog 2021-02-14 10:17:34 +01:00
Alejandro Celaya
c95cb144a8 Added margin option to QR code component 2021-02-14 10:16:30 +01:00
Alejandro Celaya
f9da22c5a1 Added support for margin param in buildQrCodeUrl function 2021-02-14 09:50:26 +01:00
Alejandro Celaya
be085f50e0 Updated to bootstrap 4.6 2021-02-14 09:29:24 +01:00
Alejandro Celaya
1122f4e560 Merge pull request #381 from acelaya-forks/feature/qr-code-improvements
Feature/qr code improvements
2021-01-24 18:38:51 +01:00
Alejandro Celaya
ecefa22204 Replace nested ternary conditions with ramda's cond 2021-01-24 18:21:04 +01:00
Alejandro Celaya
e2ba63ff58 Updated changelog 2021-01-24 18:14:19 +01:00
Alejandro Celaya
277069a0af Fixed warnings in SortingDropdown 2021-01-24 18:12:16 +01:00
Alejandro Celaya
0c9434b555 Created CopyToClipboardIcon test 2021-01-24 18:06:11 +01:00
Alejandro Celaya
0fce6dd821 Fixed warnings in DropdownBtn test 2021-01-24 18:05:37 +01:00
Alejandro Celaya
4b8e5bf3fc Added qrCode helper test 2021-01-24 17:47:03 +01:00
Alejandro Celaya
3546a17575 Improved QR code modal, to allow selecting size, format and copy URL 2021-01-24 17:37:31 +01:00
Alejandro Celaya
556495ea7e Improved QR code component 2021-01-21 16:51:54 +01:00
Alejandro Celaya
e9cef8a029 Merge pull request #375 from acelaya-forks/feature/servers-import-error
Feature/servers import error
2020-12-30 21:04:59 +01:00
Alejandro Celaya
e577eb48d6 Changed env for github workflows from ubuntu-latest to ubuntu-20.04 2020-12-30 20:58:36 +01:00
Alejandro Celaya
d08a69954a Updated changelog 2020-12-30 20:53:14 +01:00
Alejandro Celaya
fe81bfccef Fixed importing servers in android due to wrong mime type 2020-12-30 20:52:05 +01:00
Alejandro Celaya
4869435aca Changed linting order 2020-12-30 20:10:37 +01:00
Alejandro Celaya
0822cebb10 Merge pull request #374 from acelaya-forks/feature/responsive-table
Feature/responsive table
2020-12-30 20:09:42 +01:00
Alejandro Celaya
01a18f2342 Updated changelog 2020-12-30 20:05:53 +01:00
Alejandro Celaya
a22274f382 Increased breakpoint in which short URLs table collapses 2020-12-30 20:05:04 +01:00
Alejandro Celaya
c0098ac7fd Merge pull request #373 from acelaya-forks/feature/ui-fixes
Fixed minor UI glitches in visits section
2020-12-30 19:52:24 +01:00
Alejandro Celaya
ba5a99dc2a Fixed minor UI glitches in visits section 2020-12-30 19:48:02 +01:00
Alejandro Celaya
1927ad2d3a Merge pull request #370 from acelaya-forks/feature/stryker-updates
Updated stryker
2020-12-25 19:13:57 +01:00
Alejandro Celaya
0356a0204d Updated stryker 2020-12-25 19:10:35 +01:00
Alejandro Celaya
3bf64bee1e Merge pull request #369 from acelaya-forks/feature/consistent-dropdowns
Feature/consistent dropdowns
2020-12-25 11:25:48 +01:00
Alejandro Celaya
da484374a1 Renamed Dropdnown.scss to DropdownBtn.scss for consistency with component 2020-12-25 11:21:39 +01:00
Alejandro Celaya
7b9447b717 Updated changelog 2020-12-25 11:17:57 +01:00
Alejandro Celaya
e583eb2759 Ensured sorting dropdown for short URLs is not enclosed inside card 2020-12-25 11:15:49 +01:00
Alejandro Celaya
93b4de60f6 Improved sorting dropdown to display order field and order dir 2020-12-25 11:06:10 +01:00
Alejandro Celaya
16f4f7eac8 Reused dropdown-btn styles in sorting dropdown 2020-12-25 10:54:49 +01:00
Alejandro Celaya
90d4fe72db Renamed Dropdown component to DropdownBtn 2020-12-25 10:43:36 +01:00
Alejandro Celaya
e1298cfa81 Created Dropdown test 2020-12-25 10:39:54 +01:00
Alejandro Celaya
6be3a1223f Created common Dropdown component for style consistency 2020-12-25 10:29:25 +01:00
Alejandro Celaya
81d24432a9 Updated app gif 2020-12-24 10:58:59 +01:00
Alejandro Celaya
1d193f1187 Updated nginx version in docker image 2020-12-22 10:23:27 +01:00
Alejandro Celaya
c56994c813 Merge pull request #363 from acelaya-forks/feature/refactorings
Feature/refactorings
2020-12-22 10:17:59 +01:00
Alejandro Celaya
44862073bb Added v3 to changelog 2020-12-22 10:06:24 +01:00
Alejandro Celaya
9eb9182c21 Created ShlinkApiError test 2020-12-22 10:05:32 +01:00
Alejandro Celaya
b2abfd543e Moved Shlink API services to api module 2020-12-22 09:57:09 +01:00
Alejandro Celaya
8c6eaf2f1d Moved API types and type helpers to api module 2020-12-22 09:49:18 +01:00
Alejandro Celaya
811544d7df Moved api utils to subfolder 2020-12-22 09:24:33 +01:00
Alejandro Celaya
9fdfdf865e Merge pull request #361 from acelaya-forks/feature/errors-improvements
Feature/errors improvements
2020-12-22 00:00:04 +01:00
Alejandro Celaya
6a354c277c Set API response as per Shlink v2 2020-12-21 23:55:54 +01:00
Alejandro Celaya
89f6c6c283 Updated changelog 2020-12-21 23:53:15 +01:00
Alejandro Celaya
d534a4e441 Moved logic to parse API errors to a helper function 2020-12-21 23:51:49 +01:00
Alejandro Celaya
4c3772d5c8 Added meaningful error messages for the rest of API calls 2020-12-21 23:41:50 +01:00
Alejandro Celaya
ee95d5a1b7 Improved handling of errors in several API interactions 2020-12-21 21:26:45 +01:00
Alejandro Celaya
51379eb2a0 Created component holding the logic to render Shlink API errors 2020-12-21 21:19:02 +01:00
Alejandro Celaya
f69f791790 Improved handling of short URL deletion errors 2020-12-21 21:02:30 +01:00
Alejandro Celaya
54b1ab12cd Passed API error while creating URLs to display proper error messages 2020-12-21 20:55:52 +01:00
Alejandro Celaya
18d417e78c Merge pull request #359 from acelaya-forks/feature/message-improvements
Feature/message improvements
2020-12-21 18:31:21 +01:00
Alejandro Celaya
7a48a06442 Normalized import 2020-12-21 18:20:59 +01:00
Alejandro Celaya
195aaa8be6 Updated changelog 2020-12-21 18:15:09 +01:00
Alejandro Celaya
94d2f3167b Created Result test 2020-12-21 18:14:11 +01:00
Alejandro Celaya
344f5e9b0d Updated Result component so that it has the text centered by default 2020-12-21 17:58:46 +01:00
Alejandro Celaya
b211a29fc5 Created new Result component to display operation result messages consistently 2020-12-21 17:54:20 +01:00
Alejandro Celaya
c25355c531 Added Message test 2020-12-21 09:57:46 +01:00
Alejandro Celaya
5cf0c86a14 Normalized Message component, making it autocontained 2020-12-21 09:22:13 +01:00
Alejandro Celaya
852e791c80 Merge pull request #357 from acelaya-forks/feature/routable-visits-sections
Feature/routable visits sections
2020-12-20 20:01:04 +01:00
Alejandro Celaya
f5d03ed3a2 Created query helper test 2020-12-20 19:51:43 +01:00
Alejandro Celaya
4642e07fd3 Reduced duplication when defining routes in visits section 2020-12-20 19:42:37 +01:00
Alejandro Celaya
83221c1066 Added routes to subsections in visits 2020-12-20 19:28:14 +01:00
Alejandro Celaya
214b952e84 Merge pull request #356 from acelaya-forks/feature/welcome-ui
Feature/welcome UI
2020-12-20 12:39:18 +01:00
Alejandro Celaya
42adbb3739 Updated changelog 2020-12-20 12:32:54 +01:00
Alejandro Celaya
9e63c463ca Styled scroll in servers list for home page 2020-12-20 12:25:17 +01:00
Alejandro Celaya
260a6c4940 Improved welcome screen 2020-12-20 12:17:12 +01:00
Alejandro Celaya
fa949cde12 Simplified onTagClick handling in ShortUrlsTable 2020-12-20 09:09:22 +01:00
Alejandro Celaya
23da0328ec Added Shlink logo as react component 2020-12-20 08:56:46 +01:00
Alejandro Celaya
7da634e772 Fixed tags filtering from overview page 2020-12-19 22:49:11 +01:00
Alejandro Celaya
79f7459d77 Merge pull request #354 from acelaya-forks/feature/ci-migration
Replaced scrutinizer with codecov
2020-12-19 12:58:43 +01:00
Alejandro Celaya
4002392b12 Replaced scrutinizer with codecov 2020-12-19 12:55:30 +01:00
Alejandro Celaya
e9e53bb69b Added border on top of overview section cards 2020-12-17 18:42:43 +01:00
Alejandro Celaya
623deec973 Merge pull request #353 from acelaya-forks/feature/more-ui-improvements
Feature/more UI improvements
2020-12-15 19:04:04 +01:00
Alejandro Celaya
3453d4ffd5 Fixed coding styles 2020-12-15 18:59:14 +01:00
Alejandro Celaya
f9ef7eccf8 Updated changelog 2020-12-15 18:53:41 +01:00
Alejandro Celaya
3cdcffaac3 Fixed mutation checks step in ci workflow 2020-12-15 18:45:15 +01:00
Alejandro Celaya
0f23cdcd21 Updated initial interval for visits to be last 30 days 2020-12-15 18:40:36 +01:00
Alejandro Celaya
9dc6c756f2 Fixed rendering of cards in overview page 2020-12-15 18:12:15 +01:00
Alejandro Celaya
0491694839 Set fixed width to aside menu 2020-12-15 17:57:24 +01:00
Alejandro Celaya
f1f3c3f98b Merge pull request #350 from acelaya-forks/feature/ui-improvements
Feature/UI improvements
2020-12-15 10:05:16 +01:00
Alejandro Celaya
ec3ad8412c Fixed mutation tests in ci workflow 2020-12-15 10:01:15 +01:00
Alejandro Celaya
d39512732a Fixed DateRangeSelector focus state 2020-12-15 09:54:45 +01:00
Alejandro Celaya
95abf4f898 Updated changelog 2020-12-14 23:36:58 +01:00
Alejandro Celaya
61a1087d91 Added date range selector to short URLs list 2020-12-14 23:35:31 +01:00
Alejandro Celaya
3f245a757e Created DateRangeSelector test 2020-12-14 23:15:06 +01:00
Alejandro Celaya
4e236a80de Created new dropdown component to select relative or absolute date ranges 2020-12-14 22:58:15 +01:00
Alejandro Celaya
288f6e2cf8 Fixed rendering of ShlinkVersions component to match current layout 2020-12-14 19:05:25 +01:00
Alejandro Celaya
9b6d4a4d97 Added max-width to internal container 2020-12-14 18:39:19 +01:00
Alejandro Celaya
f2a8865679 Added new card styles to error pages 2020-12-13 20:57:00 +01:00
Alejandro Celaya
017db18e70 Removed unneeded step in ci workflow 2020-12-13 10:38:03 +01:00
Alejandro Celaya
19c4a61524 Added github and docker logos to badges 2020-12-13 09:32:37 +01:00
Alejandro Celaya
f01c9bd5c8 Fixed build badge 2020-12-13 06:47:46 +01:00
Alejandro Celaya
2a5fa54ae1 Merge pull request #348 from acelaya-forks/feature/github-actions
Created workflow for ci in github actions
2020-12-12 21:48:23 +01:00
Alejandro Celaya
7a1b6367a8 Changed build badges to point to github action 2020-12-12 21:43:16 +01:00
Alejandro Celaya
058860737e Removed travis-specific env vars from github action 2020-12-12 21:39:51 +01:00
Alejandro Celaya
20f2fd1080 Created workflow for ci in github actions 2020-12-12 21:29:25 +01:00
Alejandro Celaya
16ce1d24af Merge pull request #347 from acelaya-forks/feature/visits-improvements
Feature/visits improvements
2020-12-12 21:15:16 +01:00
Alejandro Celaya
a51db38749 Updated changelog 2020-12-12 21:07:32 +01:00
Alejandro Celaya
6090f97347 Updated tabs in visits section to be sticky 2020-12-12 21:05:54 +01:00
Alejandro Celaya
c74355e363 Improved visits section so that charts are grouped in sub tabs 2020-12-12 20:45:23 +01:00
Alejandro Celaya
a013d40bf1 More standardization color changes 2020-12-12 16:55:01 +01:00
Alejandro Celaya
7f7473c348 Merge pull request #346 from acelaya-forks/feature/drop-shlink-1-support
Dropped support for Shlink 1
2020-12-12 13:50:38 +01:00
Alejandro Celaya
df6f1b984f Dropped support for Shlink 1 2020-12-12 13:43:16 +01:00
Alejandro Celaya
b9905c8bf4 Ensured visits amount card displays warning for old shlink versions 2020-12-12 13:22:11 +01:00
Alejandro Celaya
32957835b3 Merge pull request #345 from acelaya-forks/feature/restyle
Feature/restyle
2020-12-12 12:13:52 +01:00
Alejandro Celaya
2efc5feb3f Updated changelog 2020-12-12 12:07:51 +01:00
Alejandro Celaya
526fa14dce Improved NoMenuLayout, using a container-xl style 2020-12-12 12:04:20 +01:00
Alejandro Celaya
4d969b994e Improved server form 2020-12-12 11:43:16 +01:00
Alejandro Celaya
d62edb2249 Moved 'add server' button inside servers dropdown 2020-12-12 11:29:15 +01:00
Alejandro Celaya
bc82e7e7fd Fixed colors in visits table 2020-12-12 11:11:36 +01:00
Alejandro Celaya
1e460d3ef7 Updated edit URL modal to be large 2020-12-12 11:07:05 +01:00
Alejandro Celaya
143a05cab1 Restyled cards, background and shadows 2020-12-12 10:56:10 +01:00
Alejandro Celaya
bf1b59c0d8 Merge pull request #343 from acelaya-forks/feature/overview-page
Feature/overview page
2020-12-08 19:44:25 +01:00
Alejandro Celaya
5ab38027bf Updated changelog 2020-12-08 19:38:35 +01:00
Alejandro Celaya
3e6aee47e5 Fixed TS compilation in tests 2020-12-08 19:36:47 +01:00
Alejandro Celaya
60282281a3 Grouped basic components in 'create' form in its own card 2020-12-08 19:21:31 +01:00
Alejandro Celaya
2017ee7456 Created SimpleCard component to reduce duplicated code when rendering cards 2020-12-08 19:10:29 +01:00
Alejandro Celaya
e60d241fcf Changed 'create' page, grouping components and adding more explanations 2020-12-08 18:52:18 +01:00
Alejandro Celaya
43af6fdaba Added redirect from server base path to overview page, to ease changing default page 2020-12-08 18:27:36 +01:00
Alejandro Celaya
f359a16004 Ensured tags input looks as a large input 2020-12-08 18:18:16 +01:00
Alejandro Celaya
1b413fb0b7 Created Overview component test 2020-12-08 17:51:49 +01:00
Alejandro Celaya
20a9259109 Minor style improvements in overview page 2020-12-08 11:39:16 +01:00
Alejandro Celaya
8d5f7e942d Implemented reducers for actions affecting short URLs list 2020-12-08 10:57:27 +01:00
Alejandro Celaya
17d5c4327b Added form to create short URLs to overview page 2020-12-07 20:37:03 +01:00
Alejandro Celaya
9b30a82a79 Created visitsOverview reducer test 2020-12-07 19:19:37 +01:00
Alejandro Celaya
a0ec3c0293 Improved wording 2020-12-07 13:03:47 +01:00
Alejandro Celaya
d9e39eee2b Added new reducer for visits overview, and added it to overview page 2020-12-07 12:12:39 +01:00
Alejandro Celaya
032e9c53f3 Extracted short URLs table into reusable component to use both on list section and overview section 2020-12-07 11:17:19 +01:00
Alejandro Celaya
dba0ac6442 Created Overview page as default page after connecting to a server 2020-12-06 18:37:22 +01:00
Alejandro Celaya
920effb4c6 Merge pull request #341 from acelaya-forks/feature/validate-flag
Feature/validate flag
2020-12-06 13:21:08 +01:00
Alejandro Celaya
bd6e455cd6 Fixed import 2020-12-06 13:20:16 +01:00
Alejandro Celaya
b9fc906537 Fixed alignment and margins for checkboxes in create form 2020-12-06 13:14:43 +01:00
Alejandro Celaya
1415f196bb Updated changelog 2020-12-06 13:09:06 +01:00
Alejandro Celaya
8f7e356e54 Added support to enable/disable validating the URL while it is created 2020-12-06 13:07:44 +01:00
Alejandro Celaya
0ed88079ad Skip install step when building docker image in travis 2020-12-06 11:57:14 +01:00
Alejandro Celaya
5182f9d147 Merge pull request #339 from acelaya-forks/feature/domains-dropdown
Feature/domains dropdown
2020-11-28 12:48:20 +01:00
Alejandro Celaya
4e1579832e Updated changelog 2020-11-28 12:38:16 +01:00
Alejandro Celaya
ff48c0cd45 Added DomainSelector test 2020-11-28 12:36:40 +01:00
Alejandro Celaya
02c7125236 Created domainsList reducer test 2020-11-28 12:22:52 +01:00
Alejandro Celaya
dc397d4b82 Improved existing tests 2020-11-28 11:45:04 +01:00
Alejandro Celaya
2a206f11b9 Renamed DomainsDropdown to DomainSelector 2020-11-28 09:58:05 +01:00
Alejandro Celaya
369fcf2f6a Improved design on domains dropdown 2020-11-28 09:34:41 +01:00
Alejandro Celaya
983e4db3b1 Created component to allow selecting from existing domains list 2020-11-25 21:05:27 +01:00
Alejandro Celaya
2a7c2474cd Merge pull request #336 from acelaya-forks/feature/fix-visits
Feature/fix visits
2020-11-14 13:09:51 +01:00
Alejandro Celaya
c890124e67 Updated changelog 2020-11-14 13:02:28 +01:00
Alejandro Celaya
3e21cccb14 Fixed visits getting accumulated every time the visits page is opened 2020-11-14 13:01:35 +01:00
Alejandro Celaya
dafebc3df9 Merge pull request #332 from acelaya-forks/feature/update-deps
Feature/update deps
2020-11-14 12:21:49 +01:00
Alejandro Celaya
6619e7cdb6 Updated changelog 2020-11-14 12:13:28 +01:00
Alejandro Celaya
c54f314424 Updated react-datepicker to latest version 2020-11-14 12:10:42 +01:00
Alejandro Celaya
4964f28169 Updated more production dependencies 2020-11-14 11:00:41 +01:00
Alejandro Celaya
dead22c332 Updated reactstrap 2020-11-14 10:33:32 +01:00
Alejandro Celaya
aba65346b4 Updated react-dev-utils 2020-11-14 10:24:15 +01:00
Alejandro Celaya
4621246cec Updated color-picker and fixed error when left open and modal is closed 2020-11-14 09:16:26 +01:00
Alejandro Celaya
f83280068b Updated more dev dependencies 2020-11-14 08:59:20 +01:00
Alejandro Celaya
0671fa6567 Updated to stryker v4 2020-11-13 23:06:03 +01:00
Alejandro Celaya
5c80e853c6 #325 Updated to Typescript 4 2020-11-13 22:46:17 +01:00
Alejandro Celaya
6c90d7072f #325 Updated to react 17 2020-11-13 22:44:26 +01:00
Alejandro Celaya
18bccab27a Moved to official docker github actions for docker-image-build 2020-11-10 19:25:20 +01:00
Alejandro Celaya
b9213952d3 Added npm ci when generating release 2020-11-01 10:39:09 +01:00
Alejandro Celaya
f1ae68a300 Allow empty changelog when publishing release 2020-11-01 10:34:53 +01:00
Alejandro Celaya
3f0409f25a Merge pull request #331 from acelaya-forks/feature/automatic-release
Feature/automatic release
2020-11-01 10:32:41 +01:00
Alejandro Celaya
6f568a16bf Moved tag releasing from travis to github workflow 2020-11-01 10:27:33 +01:00
Alejandro Celaya
39ae3b4762 Updated chanegelog to more strictly endorse to keepachangelog spec 2020-11-01 10:21:44 +01:00
Alejandro Celaya
14e31ed2c3 Merge pull request #330 from acelaya-forks/feature/fix-switch-alignment
Removed hardcoded display: inline for BooleanControls
2020-10-31 17:28:19 +01:00
Alejandro Celaya
ff1fb0dd12 Removed hardcoded display: inline for BooleanControls 2020-10-31 17:18:51 +01:00
Alejandro Celaya
2e6a35181d Merge pull request #329 from acelaya-forks/feature/fix-too-long-cache
Feature/fix too long cache
2020-10-31 13:47:43 +01:00
Alejandro Celaya
22cca598ca Updated changelog 2020-10-31 13:38:37 +01:00
Alejandro Celaya
de906bf370 Added proper caching rules to nginx config included in docker image 2020-10-31 13:36:53 +01:00
Alejandro Celaya
498594c31b Deleted service worker 2020-10-31 13:22:39 +01:00
Alejandro Celaya
cfbd246cfc Merge pull request #327 from acelaya-forks/feature/dart-sass
Feature/dart sass
2020-10-31 13:07:52 +01:00
Alejandro Celaya
3f91c556e4 Explicitly installed node 14 in scrutinizer env 2020-10-31 13:00:09 +01:00
Alejandro Celaya
4d1622607c Enabled all platforms back on docker image builds 2020-10-31 12:34:42 +01:00
Alejandro Celaya
eacdee293c Replaced node-sass with dart-sass 2020-10-31 12:27:24 +01:00
Alejandro Celaya
f4b115cffd Merge pull request #326 from acelaya-forks/feature/node-14
Updated to node 14
2020-10-31 12:08:35 +01:00
Alejandro Celaya
7dcd623513 Updated to node 14 2020-10-31 11:58:07 +01:00
Alejandro Celaya
8b00d1aaae Updated reference from travis-ci.org to travis-ci.com 2020-10-31 09:11:43 +01:00
Alejandro Celaya
facfd33e96 Merge pull request #319 from acelaya-forks/feature/calendar-z-index
Feature/calendar z index
2020-10-03 11:28:20 +02:00
Alejandro Celaya
a841dc7531 Updated changelog 2020-10-03 11:23:08 +02:00
Alejandro Celaya
28ebd55b69 Fixed z-index in react-datepicker 2020-10-03 11:22:21 +02:00
Alejandro Celaya
3eade5a0c0 Merge pull request #318 from acelaya-forks/feature/manifest-basic-auth
Feature/manifest basic auth
2020-10-03 11:10:57 +02:00
Alejandro Celaya
caf74cd87b Updated changelog 2020-10-03 11:03:17 +02:00
Alejandro Celaya
049510f513 Added crossorigin=use-credentials to manifest.json, so that credentials are passed and it is propery downloaded 2020-10-03 11:00:56 +02:00
Alejandro Celaya
b151b7eedb Added missing condition for github release to work on travis build 2020-09-20 12:46:51 +02:00
Alejandro Celaya
4e22e9c092 Added script step to publish release travis job 2020-09-20 12:38:47 +02:00
Alejandro Celaya
793883148a Added v2.6 to changelog 2020-09-20 12:07:16 +02:00
Alejandro Celaya
8acb7bea24 Merge pull request #310 from acelaya-forks/feature/line-chart-highlights
Feature/line chart highlights
2020-09-20 12:05:06 +02:00
Alejandro Celaya
335cceeb82 Fixed coding styles 2020-09-20 11:58:40 +02:00
Alejandro Celaya
bf7455ad6e Updated changelog 2020-09-20 11:49:19 +02:00
Alejandro Celaya
421cc5b718 Put together all chart-related helper functions 2020-09-20 11:46:07 +02:00
Alejandro Celaya
78d97a64aa Added visits highlightning capabilities to line chart 2020-09-20 11:43:24 +02:00
Alejandro Celaya
749c757cbd Removed unneeded condition on travis deploy step, as the same condition is on the parent job 2020-09-20 10:43:21 +02:00
Alejandro Celaya
faf9554286 Added lines between job definitions in travis config 2020-09-19 18:52:49 +02:00
Alejandro Celaya
b7a0be3872 Allowed mutation testing step to fail without failing the whole build 2020-09-19 18:08:16 +02:00
Alejandro Celaya
cff8046ff8 Merge pull request #308 from acelaya-forks/feature/contributing
Added CONTRIBUTING.md file
2020-09-19 17:42:28 +02:00
Alejandro Celaya
af1289752d Added reference to the CONTRIBUTING file from the README file 2020-09-19 17:42:06 +02:00
Alejandro Celaya
b06d9d3bc7 Added CONTRIBUTING.md file 2020-09-19 17:32:54 +02:00
Alejandro Celaya
b2904189ef Merge pull request #307 from acelaya-forks/feature/parallel-builds
Feature/parallel builds
2020-09-19 17:04:52 +02:00
Alejandro Celaya
5c639d241b Disabled docker image build in arm archs, as it fails with node-sass 2020-09-19 16:58:01 +02:00
Alejandro Celaya
984e9f32a5 Split all scripts in travis build into individual jobs 2020-09-19 16:57:35 +02:00
Alejandro Celaya
59d23b584a Merge pull request #304 from acelaya-forks/feature/gh-action-docker-build
Feature/gh action docker build
2020-09-19 16:35:29 +02:00
Alejandro Celaya
a7d865228a Updated name of branch in which docker build action needs to run 2020-09-19 16:25:33 +02:00
Alejandro Celaya
260ff716d7 Updated changelog 2020-09-19 16:24:43 +02:00
Alejandro Celaya
9001a3da37 Moved docker build to github actions, enabling multi-arch support 2020-09-19 16:23:39 +02:00
Alejandro Celaya
46db1e39f3 Merge pull request #303 from acelaya-forks/feature/cancel-tags-visits
Feature/cancel tags visits
2020-09-19 11:23:54 +02:00
Alejandro Celaya
6bf3fc0fd5 Updated changelog 2020-09-19 10:51:41 +02:00
Alejandro Celaya
a136543551 Fixed tags visits loading not being cancelled when the visits component gets unmounted 2020-09-19 10:50:49 +02:00
Alejandro Celaya
23046c149c Fixed visits normalization not converting empty strings into null 2020-09-19 10:31:23 +02:00
Alejandro Celaya
2951d0d75e Merge pull request #302 from acelaya-forks/feature/number-formatting
Feature/number formatting
2020-09-19 10:16:54 +02:00
Alejandro Celaya
b52e40edd3 Updated changelog 2020-09-17 18:11:03 +02:00
Alejandro Celaya
51556d76ac Fixed tests 2020-09-17 18:05:26 +02:00
Alejandro Celaya
871868f0a4 Moved common rendering chart labels code to external module for reuse 2020-09-15 22:30:31 +02:00
Alejandro Celaya
67495fa302 Added number formatting to charts 2020-09-15 22:22:56 +02:00
Alejandro Celaya
fc9341f631 Added number formatting to visits line chart 2020-09-13 11:11:17 +02:00
Alejandro Celaya
3fea8b5505 Ensured page numbers in paginators are prettified 2020-09-13 10:03:02 +02:00
Alejandro Celaya
89e3114ef3 Merge pull request #300 from acelaya-forks/feature/default-ordering
Feature/default ordering
2020-09-13 09:54:20 +02:00
Alejandro Celaya
4dc5fad8b8 Fixed coding standards 2020-09-13 09:47:29 +02:00
Alejandro Celaya
2567bdfdf0 Updated changelog 2020-09-13 09:32:02 +02:00
Alejandro Celaya
f36cf1e7b9 Updated short URL list params so that it requests dateCreated DESC ordering by default 2020-09-12 17:59:58 +02:00
Alejandro Celaya
bd88e56331 Merge pull request #299 from acelaya-forks/feature/updates-interval
Feature/updates interval
2020-09-12 13:18:23 +02:00
Alejandro Celaya
fe3e08de0f Fixed event source not being properly closed with new boundToMercureHub HOC 2020-09-12 12:06:53 +02:00
Alejandro Celaya
cfb165d240 Fixed boundToMercureHub HOC so that it clears updates intervals when unmounted 2020-09-12 11:55:49 +02:00
Alejandro Celaya
fa074f91be Updated changelog 2020-09-12 11:35:12 +02:00
Alejandro Celaya
6fc4963663 Replaced redux action to create one visit by action that allows multiple visits at once 2020-09-12 11:31:44 +02:00
Alejandro Celaya
ad437f655e Added support to dispatch all UI actions based on mercure bindings on a specific schedule instead of real time 2020-09-12 08:52:03 +02:00
Alejandro Celaya
9b45513684 Added form controls to set real time updates interval 2020-09-09 19:16:04 +02:00
Alejandro Celaya
5d6d802d64 Moved mercure hub binding from custom hook to HOC 2020-09-06 19:41:15 +02:00
Alejandro Celaya
536d49aac9 Merge pull request #298 from acelaya-forks/feature/always-visible-versions
Feature/always visible versions
2020-09-06 13:20:17 +02:00
Alejandro Celaya
796c9ca140 Fixed changelog 2020-09-06 13:11:33 +02:00
Alejandro Celaya
1b1a1f3230 Created component decorator that resets selected server and used it on Settings 2020-09-06 13:10:30 +02:00
Alejandro Celaya
98d856700c Added missing row wrapping Message component 2020-09-06 12:47:14 +02:00
Alejandro Celaya
b814f500de Moved shlink versions to the outer element so that's always visible 2020-09-06 12:36:17 +02:00
Alejandro Celaya
90abf29db9 Merge pull request #296 from acelaya-forks/feature/typescript
Feature/typescript
2020-09-06 10:29:55 +02:00
Alejandro Celaya
d064eb5f9e Fixed inconsistent type 2020-09-06 10:22:21 +02:00
Alejandro Celaya
58c9ef9b51 Updated dependencies 2020-09-06 10:17:46 +02:00
Alejandro Celaya
125b13e059 Added stryker mutator for TS 2020-09-06 09:46:07 +02:00
Alejandro Celaya
dcdee8b308 Simplified eslint config 2020-09-06 09:32:16 +02:00
Alejandro Celaya
f33d1fca39 Updated pattern for code coverage collection 2020-09-06 09:18:02 +02:00
Alejandro Celaya
7e907ba9b6 Updated changelog 2020-09-05 09:03:40 +02:00
Alejandro Celaya
3cec2efbbd Removed no longer used dependencies 2020-09-05 08:57:50 +02:00
Alejandro Celaya
d4094e66b3 Finished TS migration 2020-09-05 08:49:18 +02:00
Alejandro Celaya
73b854037d Migrated to TS all visits components except the biggest two 2020-09-04 19:33:16 +02:00
Alejandro Celaya
f2e7a2161d Removed duplicated code on mercure-bound components 2020-09-04 19:05:41 +02:00
Alejandro Celaya
260ed3041a Finished migrating visits helpers to TS 2020-09-04 18:43:26 +02:00
Alejandro Celaya
8a146021dd Migrated first charts to TS 2020-09-03 20:34:22 +02:00
Alejandro Celaya
4083592212 Fixed coding styles and ensured linting command applies to ts and tsx files 2020-09-02 20:27:50 +02:00
Alejandro Celaya
f9c57ca659 Migrated first visits helper components to TS 2020-09-02 20:13:31 +02:00
Alejandro Celaya
d0d664ef79 Migrated VisitsParser to TS 2020-09-02 19:32:07 +02:00
Alejandro Celaya
16d96efa4a Finished migrating all remaining utils to TS 2020-08-31 18:38:27 +02:00
Alejandro Celaya
f8ea1ae3d5 Migrated remaining tags-related elements to TS 2020-08-30 20:48:09 +02:00
Alejandro Celaya
18883caa6d Migrated tags helpers to TS 2020-08-30 20:31:31 +02:00
Alejandro Celaya
84fc82b74e Fixed custom slug field not being disabled when selecting a short code length 2020-08-30 19:52:40 +02:00
Alejandro Celaya
8a9c694fbc Migrated all remaining short-url elements to TS 2020-08-30 19:45:17 +02:00
Alejandro Celaya
4b33d39d44 Finished migrating ll short-url helpers to TS 2020-08-30 09:59:14 +02:00
Alejandro Celaya
c0f5d9c12c Finished migrating servers module to TS 2020-08-29 20:20:45 +02:00
Alejandro Celaya
ef630af154 Migrated ShlinkApiClient to TS 2020-08-29 19:51:14 +02:00
Alejandro Celaya
ebd7a76896 Migrated to TS main services except ShlinkApiClient 2020-08-29 18:51:03 +02:00
Alejandro Celaya
64a968711c Migrated all servers services to TS 2020-08-29 14:16:37 +02:00
Alejandro Celaya
aee4c2d02f Migrated to TS all servers helpers 2020-08-29 13:51:53 +02:00
Alejandro Celaya
8cc0695ee9 Refactored ServerError to infer error message based on provided server type guards 2020-08-29 10:53:02 +02:00
Alejandro Celaya
f40ad91ea9 Migrated some common components and their dependencies to TS 2020-08-29 09:19:15 +02:00
Alejandro Celaya
a96539129d Migrated more common components to TS 2020-08-28 20:05:01 +02:00
Alejandro Celaya
dcf72e6818 Finished migrating remaining reducers to TS 2020-08-28 18:33:37 +02:00
Alejandro Celaya
54290d4c9a Migrated ShlinkApiClientBuilder to TS 2020-08-27 22:09:16 +02:00
Alejandro Celaya
eb3775859a Migrated tags reducers to typescripts 2020-08-27 19:12:09 +02:00
Alejandro Celaya
83531666de Migrated to typescript the most complex reducer in the project 2020-08-27 18:31:56 +02:00
Alejandro Celaya
f3a2535e2f Defined visits TS types 2020-08-27 17:56:48 +02:00
Alejandro Celaya
f283dc8569 Migrated short URL helper modal components to TS 2020-08-26 20:37:36 +02:00
Alejandro Celaya
b19bbee7fc More components migrated to TS 2020-08-26 20:03:23 +02:00
Alejandro Celaya
1b03d04318 Migrated more short-url reducers to TS 2020-08-26 18:55:40 +02:00
Alejandro Celaya
6696fb13d6 Created redux test 2020-08-25 20:23:12 +02:00
Alejandro Celaya
f04aece7df Removed dependency on redux-actions for all reducers already migrated to typescript 2020-08-25 19:42:15 +02:00
Alejandro Celaya
d8f3952920 Migrated first short URL reducers to typescript 2020-08-24 18:52:52 +02:00
Alejandro Celaya
fefa4e7848 Migrated settings module to TS 2020-08-24 17:32:20 +02:00
Alejandro Celaya
0b4a348969 Migrated remoteServers reducer to TS 2020-08-23 11:58:43 +02:00
Alejandro Celaya
3e2fee0df5 Migrated selectedServer test to typescript 2020-08-23 10:58:43 +02:00
Alejandro Celaya
294888454d Renamed NewServerData to ServerData, as it's used in other contexts too 2020-08-23 10:52:37 +02:00
Alejandro Celaya
1b7e1e2b5b Tweaked server types and data 2020-08-23 10:51:42 +02:00
Alejandro Celaya
dc78138066 Migrate servers reducer to typescript 2020-08-23 10:20:31 +02:00
Alejandro Celaya
87e64e5899 Migrated first reducer to typescript, adding also type for the shared app state 2020-08-23 09:52:09 +02:00
Alejandro Celaya
e193a692e8 Migrated all service providers to typescript 2020-08-23 09:03:44 +02:00
Alejandro Celaya
2eba607874 More elements migrated to typescript 2020-08-22 19:03:25 +02:00
Alejandro Celaya
62df46d648 Refactored many helpers to Typescript 2020-08-22 18:32:48 +02:00
Alejandro Celaya
7c67fa4149 Migrate CreateServer component to Typescript 2020-08-22 17:58:44 +02:00
Alejandro Celaya
2db85c2783 Migrated to typescript first component getting another component with props injected 2020-08-22 13:41:54 +02:00
Alejandro Celaya
39663ba936 Migrated to TS first component where some dependency was being injected 2020-08-22 11:20:27 +02:00
Alejandro Celaya
eefea0c37b Added babel plugins to support latest TS functionalities 2020-08-22 11:00:11 +02:00
Alejandro Celaya
d65a6ba970 Migrated to Typescript a file which is imported in JS files 2020-08-22 09:48:55 +02:00
Alejandro Celaya
524b0a74c6 Migrated first component and test to typescript 2020-08-22 09:15:05 +02:00
Alejandro Celaya
72de9d4ff8 Added first Typescript files 2020-08-22 08:47:19 +02:00
Alejandro Celaya
a91f1b3bd4 Fixed coding styles 2020-08-22 08:10:31 +02:00
Alejandro Celaya
343a93b984 Installed TS and updated linter 2020-08-22 08:06:41 +02:00
Alejandro Celaya
8be17cce8a Merge pull request #291 from acelaya-forks/feature/toggle-switch
Feature/toggle switch
2020-07-14 16:23:23 +02:00
Alejandro Celaya
d2f818c1ea Moved common code between Checkbox and ToggleSwitch to child component 2020-07-14 16:14:16 +02:00
Alejandro Celaya
a675d60d59 Added new ToggleSwitch component 2020-07-14 16:05:00 +02:00
Alejandro Celaya
2d96c21b50 Updated changelog 2020-07-09 17:46:44 +02:00
Alejandro Celaya
6f6ba9e34d Merge pull request #290 from MartinH0/patch-1
Added Links to Version Info
2020-07-09 17:42:39 +02:00
Alejandro Celaya
e6efda5563 Fixed wrong use of quotes 2020-07-09 17:36:26 +02:00
Alejandro Celaya
b1df1652bf Fixed ShlinkVersions test 2020-07-09 17:34:29 +02:00
Alejandro Celaya
474239c151 Moved version link to common component, and fixed coding styles 2020-07-09 17:17:19 +02:00
MartinH0
feeb212259 Update ShlinkVersions.js 2020-07-09 15:54:45 +02:00
MartinH0
90245016a0 Update ShlinkVersions.js
Hope this works now, but tests obviously fails bc it does not expect a Link
2020-07-09 15:47:09 +02:00
MartinH0
8c7616c3a7 Update ShlinkVersions.js 2020-07-09 15:33:58 +02:00
MartinH0
ea84ce9c41 Update ShlinkVersions.js 2020-07-09 15:25:53 +02:00
MartinH0
c4730ec92d Update ShlinkVersions.js 2020-07-09 15:18:48 +02:00
MartinH0
76b3d573c0 Update ShlinkVersions.js 2020-07-09 15:15:01 +02:00
MartinH0
b96f4b7a90 Update ShlinkVersions.js
Changed back ExternalLink against docs to normal closing Tag.
2020-07-09 15:04:14 +02:00
MartinH0
2a0def262d Update ShlinkVersions.js 2020-07-09 14:53:15 +02:00
MartinH0
897e35f0b8 Update ShlinkVersions.js 2020-07-09 14:43:53 +02:00
MartinH0
1c335506d8 Update ShlinkVersions.js 2020-07-09 12:58:10 +02:00
MartinH0
d46acdbd70 Added Links to Version Info
Actually it would be better if the link is just added if version info is provided. Now the Link is given always.
2020-07-07 22:10:35 +02:00
Alejandro Celaya
026bb4140e Merge branch 'main' of github.com:shlinkio/shlink-web-client into main 2020-07-06 09:31:37 +02:00
Alejandro Celaya
d80f3da55d Updated comment on issue templates 2020-07-06 09:31:11 +02:00
Alejandro Celaya
f18495a4b1 Updated comment fixing line breaks 2020-06-27 09:11:42 +02:00
Alejandro Celaya
f908da78f6 Merge pull request #287 from acelaya-forks/feature/servers-warning
Added warning to pre-configuring servers section
2020-06-22 22:04:55 +02:00
Alejandro Celaya
bc16381c90 Added warning to pre-configuring servers section 2020-06-22 21:57:58 +02:00
Alejandro Celaya
4cb7aa64cf Removed references to master branch 2020-06-20 20:03:00 +02:00
Alejandro Celaya
da6d7aea8b Merge pull request #284 from acelaya-forks/feature/simplify-travis
Removed all conditional checks in travis
2020-06-10 18:50:23 +02:00
Alejandro Celaya
b310d79110 Removed all conditional checks in travis 2020-06-10 18:43:11 +02:00
Alejandro Celaya
e26cdc11c3 Merge pull request #283 from acelaya-forks/feature/chart-legend
Feature/chart legend
2020-06-06 12:27:01 +02:00
Alejandro Celaya
fa54aa3128 Updated changelog 2020-06-06 12:17:45 +02:00
Alejandro Celaya
e31e70039d Created GraphCard test 2020-06-06 12:16:19 +02:00
Alejandro Celaya
cb761dea8f Increased default height for doughnut charts 2020-06-06 12:08:21 +02:00
Alejandro Celaya
949e0da105 Added custom responsive legend to doughnut charts 2020-06-06 11:58:25 +02:00
Alejandro Celaya
770cc59448 Extracted logic to render graph from GraphCard to DefatlChart component 2020-06-06 10:35:13 +02:00
Alejandro Celaya
72dd2bd0a7 Merge pull request #282 from acelaya-forks/feature/mercure-dup-code
Feature/mercure dup code
2020-06-06 09:47:15 +02:00
Alejandro Celaya
54733eaa18 Updated changelog 2020-06-06 09:30:39 +02:00
Alejandro Celaya
52c56f7918 Created custom react hook that binds to mercure topic 2020-06-06 09:29:43 +02:00
Alejandro Celaya
c46d5187c1 Removed duplicated code when binding to mercure by checking if enabled first 2020-06-06 09:24:05 +02:00
Alejandro Celaya
05e3e87653 Merge pull request #281 from acelaya-forks/feature/docker-version
Fixed version not properly provided to docker image
2020-06-06 08:58:41 +02:00
Alejandro Celaya
8b9289ff08 Fixed version not properly provided to docker image 2020-06-06 08:50:37 +02:00
Alejandro Celaya
16ffbcfbc0 Merge pull request #278 from acelaya-forks/feature/fix-default-grouping
Feature/fix default grouping
2020-05-31 20:30:32 +02:00
Alejandro Celaya
d825b6e174 Updated changelog 2020-05-31 20:17:59 +02:00
Alejandro Celaya
73e55cc742 Replaced if/else by functional matcher 2020-05-31 20:16:15 +02:00
Alejandro Celaya
32cc1cc580 Improved logic to determine default grouping for line chart based on how old the visits are 2020-05-31 20:03:59 +02:00
Alejandro Celaya
e00574553f Moved some helper components for visits to visits/helpers 2020-05-31 17:51:52 +02:00
Alejandro Celaya
984c1ea716 Added v2.5.0 to changelog 2020-05-31 12:00:50 +02:00
Alejandro Celaya
df38cf6ca9 Merge pull request #275 from acelaya-forks/feature/remove-class-components
Feature/remove class components
2020-05-31 11:51:08 +02:00
Alejandro Celaya
1b60b0e2a8 Updated SortableBarGraph to be a functional component 2020-05-31 11:36:56 +02:00
Alejandro Celaya
11f9c7c507 Updated TagsSelector to be a functional component 2020-05-31 11:19:53 +02:00
Alejandro Celaya
ebe649aaac Updated EditTagModal to be a functional component 2020-05-31 11:06:23 +02:00
Alejandro Celaya
656b68d422 Updated DeleteTagConfirmModal to be a functional component 2020-05-31 10:45:18 +02:00
Alejandro Celaya
cd1f186e28 Updated EditTagsModal to be a functional component 2020-05-31 10:31:00 +02:00
Alejandro Celaya
d0b3edaa2f Updated DeleteShortUrlModal to be a functional component 2020-05-31 10:23:13 +02:00
Alejandro Celaya
2268b85ade Updated CreateShortUrlResult to be a functional component 2020-05-31 10:16:09 +02:00
Alejandro Celaya
d7e3b7b912 Updated ImportServersBtn to be a functional component 2020-05-31 09:58:27 +02:00
Alejandro Celaya
4bd83eecfb Updated ScrollToTop to be a functional component 2020-05-31 09:50:00 +02:00
Alejandro Celaya
b7fd2308ad Merge pull request #274 from acelaya-forks/feature/stryker3
Updated to stryker 3
2020-05-31 09:34:02 +02:00
Alejandro Celaya
a6958941ad Updated to stryker 3 2020-05-31 09:27:42 +02:00
Alejandro Celaya
c98b28ff0f Merge pull request #273 from acelaya-forks/feature/charts-improvements
Feature/charts improvements
2020-05-31 09:19:02 +02:00
Alejandro Celaya
6a372badfa Improved labels displayed in charts when visits are highlighted 2020-05-31 09:07:21 +02:00
Alejandro Celaya
b6ab9a1bdd Improved memoization of grouped visits for line chart 2020-05-31 08:55:52 +02:00
Alejandro Celaya
daf9e7cf64 Merge pull request #272 from acelaya-forks/feature/line-chart-improvements
Some improvements on LineChartCard
2020-05-30 17:52:55 +02:00
Alejandro Celaya
ef42dcd666 Simplified code and removed duplication 2020-05-30 17:43:13 +02:00
Alejandro Celaya
1b6028ae6d Some improvements on LineChartCard 2020-05-30 17:39:08 +02:00
Alejandro Celaya
9340512980 Merge pull request #271 from acelaya-forks/feature/line-chart
Feature/line chart
2020-05-30 10:54:27 +02:00
Alejandro Celaya
9d0b4cc065 Updated changelog 2020-05-30 10:43:18 +02:00
Alejandro Celaya
c5cb0dcb26 Added test for LineChartCard 2020-05-30 10:41:46 +02:00
Alejandro Celaya
a42f5ab13e Set fixed height for time-based line chart 2020-05-30 10:26:52 +02:00
Alejandro Celaya
68b0577526 Added dynamic grouping to time-based line chart 2020-05-30 09:57:21 +02:00
Alejandro Celaya
61867366e7 Created first version of the time-based visits chart 2020-05-30 09:25:15 +02:00
Alejandro Celaya
c670d86955 Merge pull request #270 from acelaya-forks/feature/parallel-docker-build
Feature/parallel docker build
2020-05-17 10:37:34 +02:00
Alejandro Celaya
4565a64cd8 Rolled back node version for scrutinizer 2020-05-17 10:28:49 +02:00
Alejandro Celaya
f36e42d9c1 Fixed travis config error 2020-05-17 10:16:53 +02:00
Alejandro Celaya
0a3a97242b Simplified docker image building, as it will now be run in a different job as the dist file release 2020-05-17 10:13:33 +02:00
Alejandro Celaya
68253c3bc4 Disabled multi-arch building until everything is compatible 2020-05-17 10:12:39 +02:00
Alejandro Celaya
544384d85e Updated runtimne versions 2020-05-17 10:04:05 +02:00
Alejandro Celaya
91daec852f Added parallel docker image multi-arch building 2020-05-17 10:02:36 +02:00
Alejandro Celaya
dcc5b9cc8c Merge pull request #268 from acelaya-forks/feature/tag-visits
Feature/tag visits
2020-05-13 20:41:39 +02:00
Alejandro Celaya
1d26cd93fb Added real time updates to tags list page 2020-05-13 18:32:27 +02:00
Alejandro Celaya
e47dfaf36f Changed paginable charts so that they use 50 items per page by default 2020-05-11 19:44:27 +02:00
Alejandro Celaya
09e2c69e46 Ensured visits by tag route does not work for old Shlink servers 2020-05-11 19:40:19 +02:00
Alejandro Celaya
07d3567244 Added progress bar to visits page when loading a lot of visits 2020-05-11 19:32:42 +02:00
Alejandro Celaya
9bdbe90716 Ensured state is properly reset when starting, finisihing or failing to load visits 2020-05-11 18:55:35 +02:00
Alejandro Celaya
02a4380f7c Updated changelog 2020-05-10 20:36:03 +02:00
Alejandro Celaya
4e483dc5d4 Created TagVisits test 2020-05-10 20:30:19 +02:00
Alejandro Celaya
52631e629e Created TagVisitsHeader test 2020-05-10 20:23:45 +02:00
Alejandro Celaya
3a53298417 Improved visits pages titles 2020-05-10 20:17:17 +02:00
Alejandro Celaya
fb0f14fc16 Created header for visits by tag section 2020-05-10 19:49:58 +02:00
Alejandro Celaya
7a94b1730d Created common component for visits header 2020-05-10 19:37:00 +02:00
Alejandro Celaya
f856bc218a Created tagVisits reducer test 2020-05-10 19:12:18 +02:00
Alejandro Celaya
bfbb21e1cc Created page for tag visit stats 2020-05-10 19:02:58 +02:00
Alejandro Celaya
18e18f533b Extracted visits charts elements into reusable component 2020-05-10 17:49:55 +02:00
Alejandro Celaya
6eead70511 Merge pull request #266 from acelaya-forks/feature/tags-list-improvements
Feature/tags list improvements
2020-05-10 11:34:48 +02:00
Alejandro Celaya
6fd30ed51a Improved how tags are exposed by the ApiClient when listing tags 2020-05-10 11:20:40 +02:00
Alejandro Celaya
67c674f073 Updated changelog 2020-05-10 11:15:24 +02:00
Alejandro Celaya
289d8784c0 Converted TagCard component into functional component 2020-05-10 11:12:22 +02:00
Alejandro Celaya
18e026e4ca Updated tags list to display visits and short URLs when remote shlink version allows it 2020-05-10 10:57:49 +02:00
Alejandro Celaya
8741f42fe8 Merge pull request #264 from acelaya-forks/feature/charts-precision
Feature/charts precision
2020-05-07 11:21:51 +02:00
Alejandro Celaya
665d6209d9 Updated changelog 2020-05-07 11:10:10 +02:00
Alejandro Celaya
59fda29894 Added precision 0 to charts, to avoid having decimals 2020-05-07 11:06:14 +02:00
Alejandro Celaya
61c027f9a1 Merge pull request #263 from acelaya-forks/feature/minor-improvements
Minor improvements
2020-05-03 20:23:24 +02:00
Alejandro Celaya
241c9b73b0 Minor improvements 2020-05-03 20:16:21 +02:00
Alejandro Celaya
85dc1d0825 Merge pull request #260 from acelaya-forks/feature/responsive-visits-table
Feature/responsive visits table
2020-04-28 19:08:20 +02:00
Alejandro Celaya
e38887aa26 Ensured side menu is not swippoed when horizontally scrolling visits table 2020-04-28 18:54:58 +02:00
Alejandro Celaya
54fec79945 First minor improvements to the visits table responsiveness 2020-04-27 18:04:24 +02:00
Alejandro Celaya
fad0bf1c9d Merge pull request #258 from acelaya-forks/feature/redux-localstorage
Feature/redux localstorage
2020-04-27 13:49:05 +02:00
Alejandro Celaya
be2f86050f Updated changelog 2020-04-27 13:37:54 +02:00
Alejandro Celaya
a7f941e8e4 Deleted no-longer-needed ServersService 2020-04-27 13:21:07 +02:00
Alejandro Celaya
b08c6748c7 Moved remote servers loading to separated action 2020-04-27 12:54:52 +02:00
Alejandro Celaya
bdd7932e07 Refactored ServersDropdown into functional component 2020-04-27 12:30:17 +02:00
Alejandro Celaya
bcf5dcf180 Converted server handling actions into regular actions 2020-04-27 11:30:51 +02:00
Alejandro Celaya
8b2cbf7aea Some minor refactorings 2020-04-27 10:52:19 +02:00
Alejandro Celaya
277b5e43f8 Flatten model holding list of servers 2020-04-27 10:49:55 +02:00
Alejandro Celaya
7dd6a31609 Deleted SettingsService, as the task is not transparently handled by a redux middleware 2020-04-26 19:07:47 +02:00
Alejandro Celaya
86bf1515d4 Added redux middleware to save parts of the store in the local storage transparently 2020-04-26 19:04:17 +02:00
Alejandro Celaya
bbc47b387e Created single reducer to handle settings 2020-04-26 13:00:27 +02:00
Alejandro Celaya
3953e98a77 Merge pull request #257 from acelaya-forks/feature/navigate-back
Feature/navigate back
2020-04-26 12:02:54 +02:00
Alejandro Celaya
09b8bd501d Converted TagsList component into functional component 2020-04-26 11:48:08 +02:00
Alejandro Celaya
6bddaaa055 Added cancel button to edit server page 2020-04-26 10:56:27 +02:00
Alejandro Celaya
dd728d4d13 Added back button to visits stats page 2020-04-26 10:43:00 +02:00
Alejandro Celaya
9ba8bc8f3d Merge pull request #256 from acelaya-forks/feature/settings-page
Feature/settings page
2020-04-25 11:19:19 +02:00
Alejandro Celaya
16dee3664b Ensured mercure updates are not set even if supported, when they have been disabled 2020-04-25 10:37:50 +02:00
Alejandro Celaya
6fcf588bfd Updated changelog 2020-04-25 10:23:51 +02:00
Alejandro Celaya
6a6c427b0e Added unit tests for settings business logic elements 2020-04-25 10:22:17 +02:00
Alejandro Celaya
41f885d8ec Created settings page and reducers to handle real-time updates config 2020-04-25 09:49:54 +02:00
Alejandro Celaya
7516ca8dd9 Created settings page and converted MainHeader into functional component 2020-04-18 20:58:35 +02:00
Alejandro Celaya
aa59a95f91 Merge pull request #251 from acelaya-forks/feature/real-time-updates
Feature/real time updates
2020-04-18 20:57:07 +02:00
Alejandro Celaya
8a5161c0e8 Updated changelog 2020-04-18 20:36:49 +02:00
Alejandro Celaya
d8ae69e861 Added test for mercure info helpers 2020-04-18 12:49:03 +02:00
Alejandro Celaya
a485d0b507 Added token expired handling to mercure binding 2020-04-18 12:26:00 +02:00
Alejandro Celaya
ed40b79c8d Added more tests covering new use cases 2020-04-18 12:09:51 +02:00
Alejandro Celaya
91488ae294 Fixed visits count not handling separated tooltiups 2020-04-18 11:03:49 +02:00
Alejandro Celaya
a22a1938c1 Added automatic refresh on mercure events 2020-04-18 10:50:01 +02:00
Alejandro Celaya
0f73cb9f8c Converted short URLs list in functional component 2020-04-17 17:39:30 +02:00
Alejandro Celaya
f3129399de Added EventSource connection to mercure hub possible 2020-04-17 17:11:52 +02:00
Alejandro Celaya
37e6c27461 Created mercure info reducer and loaded info when server is reachable 2020-04-17 15:51:18 +02:00
Alejandro Celaya
d231ed3ede Merge pull request #247 from acelaya-forks/feature/user-agent-improvements
Feature/user agent improvements
2020-04-10 20:06:57 +02:00
Alejandro Celaya
cf6f9028f2 Some more improvements on how chart height is calculated 2020-04-10 19:57:33 +02:00
Alejandro Celaya
7cf49d2c1a Increased minimum charts height 2020-04-10 19:47:42 +02:00
Alejandro Celaya
e37fb1b4bd Updated changelog 2020-04-10 19:29:57 +02:00
Alejandro Celaya
faf5d0bf7b Unified function parsing user agent for browser and os 2020-04-10 19:22:13 +02:00
Alejandro Celaya
6fede88072 Added dependency on bowser to have a more accurate browser and OS detection 2020-04-10 19:16:44 +02:00
Alejandro Celaya
87ffbefa61 Merge pull request #246 from acelaya-forks/feature/create-improvements
Feature/create improvements
2020-04-10 18:50:09 +02:00
Alejandro Celaya
f33ae17781 Updated changelog 2020-04-10 18:43:16 +02:00
Alejandro Celaya
2a2bae6d1a Improved short URL creation 2020-04-10 18:42:08 +02:00
Alejandro Celaya
eb65e99024 Merge pull request #244 from acelaya-forks/feature/chart-visit-highlighting
Feature/chart visit highlighting
2020-04-10 15:21:07 +02:00
Alejandro Celaya
52dbeb6201 Optimized visits parser to act over the normalized list of visits 2020-04-10 14:59:12 +02:00
Alejandro Celaya
fafe920b7b Ensured highlighted stats are properly sorted and paginated on charts that support that 2020-04-10 14:38:31 +02:00
Alejandro Celaya
9d1e48ee90 Updated main list paginator to be sticky 2020-04-10 13:42:21 +02:00
Alejandro Celaya
3851342e1b Added button to reset visits selection 2020-04-10 13:27:01 +02:00
Alejandro Celaya
b863c2e19d Used cursor pointer in bar charts 2020-04-10 13:04:39 +02:00
Alejandro Celaya
ed584d19e5 Ensured charts datasets have a unique label 2020-04-10 12:57:14 +02:00
Alejandro Celaya
73256dcf5b Handled toggling between highlighted chart bars 2020-04-10 12:53:54 +02:00
Alejandro Celaya
c67a23c988 Added support to disable date inputs 2020-04-10 12:25:06 +02:00
Alejandro Celaya
8f42e65ccd Allowed visits to be selected on charts so that they get highlighted on the rest of the charts 2020-04-10 11:59:53 +02:00
Alejandro Celaya
05deb1aff0 Merge pull request #242 from acelaya-forks/feature/visits-table
Feature/visits table
2020-04-09 11:23:51 +02:00
Alejandro Celaya
a74b7cdfad Updated changelog 2020-04-09 11:00:27 +02:00
Alejandro Celaya
1c3119ee76 Allowed multiple selection on visits table 2020-04-09 10:56:54 +02:00
Alejandro Celaya
ca52911e42 Added VisitsTable test 2020-04-09 10:21:38 +02:00
Alejandro Celaya
9177bc7cef Tested how hilghlighted data behaves on GraphCards 2020-04-09 09:44:14 +02:00
Alejandro Celaya
310831a26a Converted ShortUrlVisits in functional component 2020-04-07 22:33:41 +02:00
Alejandro Celaya
8a486d991b Implemented some improvements and fixes on how visits table is split and calculated 2020-04-05 18:04:15 +02:00
Alejandro Celaya
b79333393b Converted SearchField component into funcitonal component 2020-04-05 16:18:08 +02:00
Alejandro Celaya
cb7062bb95 Created fake border with before and after pseudoelements for sticky table cells 2020-04-05 16:02:42 +02:00
Alejandro Celaya
94c5b2c471 Improved useToggle hook so that it also returns enabler and disabler 2020-04-05 12:18:41 +02:00
Alejandro Celaya
66bf26f1dc Improved highlighted data calculation so that it works with values different than 1 2020-04-05 11:57:39 +02:00
Alejandro Celaya
f5cc1abe75 Ensured info for selected visit in visits table gets highlighted in bar charts 2020-04-04 20:16:20 +02:00
Alejandro Celaya
bd4255108d Improved VisitsTable performance by memoizing visits lists 2020-04-04 12:58:04 +02:00
Alejandro Celaya
06b63d1af2 Improved rendering of visits table on mobile devices 2020-04-04 12:09:17 +02:00
Alejandro Celaya
2bd70fb9e6 Fixed unit tests 2020-04-04 10:36:38 +02:00
Alejandro Celaya
e6034dfb14 Created VisitsTable 2020-04-03 23:00:57 +02:00
Alejandro Celaya
c8ba6764c2 Merge pull request #238 from acelaya-forks/feature/edit-long-url
Feature/edit long url
2020-03-30 21:36:20 +02:00
Alejandro Celaya
19337d6c05 Added tests for elements regarding short URL edition 2020-03-30 21:26:30 +02:00
Alejandro Celaya
a6ad3c2d4d Updated changelog 2020-03-30 21:01:54 +02:00
Alejandro Celaya
b0dd885c09 Converted ShortUrlsRowMenu into functional component 2020-03-30 21:01:01 +02:00
Alejandro Celaya
2235592308 Fixed ShortUrlsRowMenu test 2020-03-30 20:50:31 +02:00
Alejandro Celaya
1219a16261 Ensured short URLs list is updated after editing the long URL of a short URL 2020-03-30 20:47:33 +02:00
Alejandro Celaya
7949e224e0 Created modal to edit the loing URL behind a short URL 2020-03-30 20:42:58 +02:00
Alejandro Celaya
ab2f311bb7 Merge pull request #237 from acelaya-forks/feature/short-code-length
Feature/short code length
2020-03-29 19:49:09 +02:00
Alejandro Celaya
a5aab43666 Updated changelog 2020-03-29 19:41:29 +02:00
Alejandro Celaya
74ebd4e572 Converted CreateShortUrl to functional component 2020-03-29 19:36:45 +02:00
Alejandro Celaya
bd29670108 Added short code length field to form to create short URLs 2020-03-29 18:55:41 +02:00
Alejandro Celaya
9a20b4428d Merge pull request #236 from acelaya-forks/feature/progressive-paginator
Feature/progressive paginator
2020-03-28 17:52:35 +01:00
Alejandro Celaya
d7da8521ce Created helper functions to determine the key and if a page is disabled on a progressive paginator 2020-03-28 17:43:09 +01:00
Alejandro Celaya
bab3b252c1 Updated changelog 2020-03-28 17:35:02 +01:00
Alejandro Celaya
7f05c5c2da Split utils module into several helpers modules 2020-03-28 17:33:27 +01:00
Alejandro Celaya
2d5c2779c3 Moved helper functions to render progressive paginators to a common place 2020-03-28 17:25:12 +01:00
Alejandro Celaya
06db4f6556 Used progressive pagination for the short URLs list 2020-03-28 17:19:33 +01:00
Alejandro Celaya
ea5ec63a22 Ensured all branches build the docker image 2020-03-22 09:34:24 +01:00
Alejandro Celaya
f46e737e77 Merge pull request #233 from acelaya-forks/feature/fix-docker-build-condition
Fixed docker build condition so that it's run for any branch or tag a…
2020-03-21 13:39:31 +01:00
Alejandro Celaya
6e63bdaafa Fixed docker build condition so that it's run for any branch or tag as long as it is not a PR 2020-03-21 08:43:35 +01:00
Alejandro Celaya
79ccef9f7e Avoid latest docker to be build when building a tag 2020-03-21 06:59:37 +01:00
Alejandro Celaya
a9653b3674 Merge pull request #232 from acelaya-forks/feature/improve-docker-build
Feature/improve docker build
2020-03-20 09:23:35 +01:00
Alejandro Celaya
b5a188e802 Improved building process so that already generated dist files are reused when building docker image is possible 2020-03-20 09:12:43 +01:00
Alejandro Celaya
38fc402b16 Improved docker build script to avoid duplicating code 2020-03-20 07:12:07 +01:00
Alejandro Celaya
584d1ec1ce Fixed conditional in docker build script 2020-03-19 20:39:34 +01:00
Alejandro Celaya
2ca7faa457 Merge pull request #230 from acelaya-forks/feature/travis-docker-build
Added docker image building as a deployment step for travis
2020-03-19 20:32:00 +01:00
Alejandro Celaya
03806abda0 Changed build steps so that mutation testing a docker build are only run on pull request builds 2020-03-19 20:26:35 +01:00
Alejandro Celaya
18d125430d Added docker image building as a deployment step for travis 2020-03-19 20:04:30 +01:00
Alejandro Celaya
f57f6b7745 Merge pull request #228 from acelaya-forks/feature/memoize-server-version
Feature/memoize server version
2020-03-16 19:01:33 +01:00
Alejandro Celaya
75ff2b8f40 Added app gif to readme 2020-03-16 18:53:06 +01:00
Alejandro Celaya
2ec04c0121 Fixed test by using different serverId every time, preventing memoization 2020-03-16 18:51:04 +01:00
Alejandro Celaya
5145a41dac Memoized the loading of the server version, assuming it will not change at runtime 2020-03-16 13:34:24 +01:00
Alejandro Celaya
25c67f1c3e Merge pull request #227 from acelaya-forks/feature/edit-servers
Feature/edit servers
2020-03-15 14:32:30 +01:00
Alejandro Celaya
77b9181150 Replaced hardcoded color by sass var 2020-03-15 14:23:57 +01:00
Alejandro Celaya
e4f7ded8e2 Updated changelog 2020-03-15 14:04:33 +01:00
Alejandro Celaya
35a62f1fb1 Added link to edit existing servers 2020-03-15 14:03:41 +01:00
Alejandro Celaya
24f2deda46 Moved common code to handle currently selected server to HOC 2020-03-15 13:43:12 +01:00
Alejandro Celaya
5d8af1a0e5 Simplified EditServer component by wrapping ServerForm 2020-03-15 12:02:19 +01:00
Alejandro Celaya
6d44ac1e0c Created common component that can be used both for create and edit servers 2020-03-15 11:59:07 +01:00
Alejandro Celaya
fb0ebddf28 Created component to edit existing servers 2020-03-15 11:29:20 +01:00
Alejandro Celaya
0aebaa4da1 Extracted logic to render horizontal form groups to their own components 2020-03-15 10:50:05 +01:00
Alejandro Celaya
f6baedc655 Converted CreateServer into functional component 2020-03-15 10:33:23 +01:00
Alejandro Celaya
7db222664d Fixed tests 2020-03-15 09:56:16 +01:00
Alejandro Celaya
8223f0fd64 Undone weird changes in package lock file 2020-03-15 09:43:42 +01:00
Alejandro Celaya
f44ec42f51 Added links to delete and edit the server when a server could not be reached 2020-03-15 09:17:33 +01:00
Alejandro Celaya
dab75ab6a9 Updated badges 2020-03-10 21:53:21 +01:00
Alejandro Celaya
01672b88e1 Merge pull request #222 from acelaya-forks/feature/server-not-found
Feature/server not found
2020-03-08 13:17:56 +01:00
Alejandro Celaya
78dc297022 Updated changelog 2020-03-08 13:05:15 +01:00
Alejandro Celaya
c8cf75fa28 Created ServerError test 2020-03-08 13:04:21 +01:00
Alejandro Celaya
b011b4e1d8 Fixed tests 2020-03-08 12:57:01 +01:00
Alejandro Celaya
9804a2d18d Added list of servers connected to store in ServerError component 2020-03-08 12:50:42 +01:00
Alejandro Celaya
d1a5ee43e9 Created components to display errors when loading a server 2020-03-08 12:41:18 +01:00
Alejandro Celaya
febecab33c Migrated Home component to a functional component 2020-03-08 11:35:06 +01:00
Alejandro Celaya
99042c0979 Extracted servers list group from home component to a reusable component 2020-03-08 11:16:57 +01:00
Alejandro Celaya
6395e4e00b Improved NotFount component so that link text is passed as children 2020-03-08 10:28:04 +01:00
Alejandro Celaya
4a69907ca3 Fixed generation of component keys to make them render properly 2020-03-08 10:16:45 +01:00
Alejandro Celaya
c8d682cc98 Handled loading server in just one place, and added error handling for loading servers 2020-03-08 10:00:25 +01:00
Alejandro Celaya
f4cc8d3a0c Fixed default value for vertically aligned items 2020-03-07 12:07:51 +01:00
Alejandro Celaya
6ac89334fd Merge pull request #220 from acelaya-forks/feature/improvements
Feature/improvements
2020-03-06 21:56:20 +01:00
Alejandro Celaya
f55d3a66aa Converted ShortUrlsRow component into a functional component 2020-03-06 21:44:03 +01:00
Alejandro Celaya
972eafab34 Updated changelog 2020-03-06 21:26:19 +01:00
Alejandro Celaya
fba156b271 Moved copy-to-clipboard control next to short URL 2020-03-06 21:25:30 +01:00
Alejandro Celaya
96d538db15 Replaced Unknown by Direct for traffic comming from undetermined referrers 2020-03-06 20:42:22 +01:00
Alejandro Celaya
b89bfa3c1c Merge pull request #215 from acelaya-forks/feature/versions
Feature/versions
2020-03-05 14:20:31 +01:00
Alejandro Celaya
73e3f42614 Added ShlinkVersions test 2020-03-05 13:55:39 +01:00
Alejandro Celaya
e761f5e1bd Updated changelog 2020-03-05 13:45:24 +01:00
Alejandro Celaya
4a6dd66ecd Added scripts to pass version when building docker image 2020-03-05 13:37:07 +01:00
Alejandro Celaya
8e1c6908c6 Updated build script so that it replaces version placeholder when a version is provided 2020-03-05 13:27:57 +01:00
Alejandro Celaya
f59e569e22 Extracted logic to determine app version from function to generate dist file 2020-03-05 13:04:12 +01:00
Alejandro Celaya
be50b24504 Added mechanism to provide a version to shlink-web-client 2020-03-05 12:53:32 +01:00
Alejandro Celaya
c181831a37 Fixed tests 2020-03-05 11:58:35 +01:00
Alejandro Celaya
dbee62ac8c Moved shlink versions component to main container 2020-03-05 11:46:38 +01:00
Alejandro Celaya
1e949b3a22 Added shlink versions to side menu 2020-03-05 11:11:26 +01:00
Alejandro Celaya
b02dcf6c53 Refactored delete server components 2020-03-05 10:18:38 +01:00
Alejandro Celaya
ab7718e335 Removed duplicated code from AsideMenu by creating an AsideMenuItem helper component 2020-03-05 10:03:38 +01:00
Alejandro Celaya
451c77d47f Merge pull request #214 from acelaya-forks/feature/consistent-server-loading
Feature/consistent server loading
2020-03-05 09:32:59 +01:00
Alejandro Celaya
fa0d3d4047 Removed no longer needed async/await when building api client 2020-03-05 09:23:53 +01:00
Alejandro Celaya
397a183f65 Converted MenuLayout into a functional component with hooks 2020-03-05 09:08:50 +01:00
Alejandro Celaya
bc8905ee7f Ensured server is properly loaded before trying to render any children component 2020-03-05 08:59:07 +01:00
Alejandro Celaya
853032ac7f Displayed preloader when a server is being loaded 2020-03-05 08:41:55 +01:00
Alejandro Celaya
3b0e282a52 Merge pull request #211 from acelaya-forks/feature/jest-each
Feature/jest each
2020-02-17 18:32:52 +01:00
Alejandro Celaya
bb28cb3862 Updated changelog 2020-02-17 18:25:21 +01:00
Alejandro Celaya
d0f458bece Uninstalled jest-each and replaced by jest's native each 2020-02-17 18:21:52 +01:00
Alejandro Celaya
da54a72b3e Merge pull request #206 from acelaya-forks/feature/match-domain
Feature/match domain
2020-02-08 11:04:58 +01:00
Alejandro Celaya
86c155d8d1 Updated changelog 2020-02-08 10:47:44 +01:00
Alejandro Celaya
666d2d3065 Ensured domain is dispatched when modifying a short URL somehow 2020-02-08 10:46:11 +01:00
Alejandro Celaya
01e69fb6ca Merge pull request #204 from acelaya-forks/feature/multi-domain-fixes
Feature/multi domain fixes
2020-02-08 10:24:49 +01:00
Alejandro Celaya
30e5253acd Simplified instructions removing redundant vars 2020-02-08 10:07:34 +01:00
Alejandro Celaya
c67ce3918b Removed redundant function call 2020-02-08 10:03:24 +01:00
Alejandro Celaya
58077f2d86 Updated changelog 2020-02-08 09:59:13 +01:00
Alejandro Celaya
098c94bccf Ensured domain is passed when deleting a short URL on a specific domain 2020-02-08 09:57:18 +01:00
Alejandro Celaya
861a3c068f Ensured domain is passed when editing meta for a short URL on a specific domain 2020-02-08 09:52:30 +01:00
Alejandro Celaya
3b95e8ebc0 Ensured domain is passed when editing tags for a short URL on a specific domain 2020-02-08 09:48:35 +01:00
Alejandro Celaya
170e427530 Ensured domain is passed when loading detail for a short URL on a specific domain 2020-02-08 09:38:19 +01:00
Alejandro Celaya
707c9f4ce6 Created VisitStatsLink test 2020-02-08 09:22:17 +01:00
Alejandro Celaya
dc672bf0f0 Ensured domain is passed when loading visits for a short URL on a specific domain 2020-02-08 09:07:55 +01:00
Alejandro Celaya
c682737505 Standardized date-picker selected day color 2020-02-02 09:30:41 +01:00
Alejandro Celaya
46fa3d4345 Merge pull request #201 from acelaya-forks/feature/document-routing-fallback
Updated documentation
2020-01-31 20:51:17 +01:00
Alejandro Celaya
9b7bc4b495 Updated documentation 2020-01-31 20:37:50 +01:00
Alejandro Celaya
4385061499 Merge pull request #200 from acelaya-forks/feature/refactor-edit-tags-modal
Feature/refactor edit tags modal
2020-01-31 20:22:04 +01:00
Alejandro Celaya
e17498e68b Updated changelog 2020-01-31 20:13:18 +01:00
Alejandro Celaya
3e298f010b Simplified DeleteShortUrlModal component and shortUrlDeletion reducer 2020-01-31 20:12:22 +01:00
Alejandro Celaya
30117bd121 Simplified EditTagsModal component and shortUrlTags reducer 2020-01-31 20:06:28 +01:00
Alejandro Celaya
93f33b6218 Fixed some tests after not injecting a component 2020-01-31 20:04:03 +01:00
Alejandro Celaya
535d08a607 Merge pull request #197 from MartinH0/master
Add htaccess to redirect if not found to index
2020-01-31 16:21:30 +01:00
MartinH0
6ac3a49db2 Updated nginx.conf (optimization for future)
1. changed location from "~" (case sensitive!) to "~*" (case insensitive!) to also match uppercase static assets. (http://nginx.org/en/docs/http/ngx_http_core_module.html#location)
2. added regex "jpe?g" to match "jpg" and "jpeg" in one command.
2020-01-31 01:36:17 +01:00
MartinH0
c16f760d79 Update .htaccess
1. removed $ (dollar sign from line 14
2. changed line 8 from ".*" to "(.*)"
2020-01-31 01:29:41 +01:00
MartinH0
965c2b243f Update .htaccess
1. added more comments.
2. added NC Tag for making all the static assets case insensetive ("jpg" now matches "jpg" and "JPG" and so on)
3. transformed "jpe|jpeg" into "jpe?g" as its regex for the same, but shorter
4. changed line 8 from "+" to "*" to match everything, also zero times the wildcard
2020-01-31 01:27:13 +01:00
MartinH0
703addddb9 updated htaccess
deleted second json (just needed once)
2020-01-30 21:05:20 +01:00
MartinH0
ab6dff5c31 Updates htaccess
return 404 error if static assets does not exist
2020-01-30 20:51:23 +01:00
MartinH0
2ef330c62b Updated htaccess 2020-01-30 20:49:46 +01:00
MartinH0
72e71aff40 Updated htaccess to meet required functions.
If a file gets called it will be redirected to index.html

But not if it the requested File does contain a dot (and with this does have a file extension.

If you call:
links.domain.de/notexistingfile.jpg 
It will trigger 404

If you call:
links.domain.de/server/[CODE-CODE-CODE]/list-short-urls/1
It will redirect the call to index.html
2020-01-30 19:06:50 +01:00
MartinH0
cefd6ec752 Add htaccess to redirect if not found to index
If (file not found or directory not found)
then > redirect to index.html
2020-01-30 18:51:38 +01:00
MartinH0
aec3de18aa Deleted .htaccess at wrong directory
Sorry fucked it up, will correct it.
2020-01-30 18:51:32 +01:00
MartinH0
97620cb583 Add htaccess to redirect if not found to index
If file not fount or directory not found redirect to index.html
2020-01-30 18:36:02 +01:00
Alejandro Celaya
cf4e8190a4 Merge pull request #195 from acelaya-forks/feature/server-version-wrapper
Feature/server version wrapper
2020-01-28 19:55:24 +01:00
Alejandro Celaya
8af7436f13 Updated changelog 2020-01-28 19:47:41 +01:00
Alejandro Celaya
c53520ae56 Moved logic to dynamically render components based on server version to a separated component 2020-01-28 19:46:36 +01:00
Alejandro Celaya
3adcaef455 Merge pull request #194 from acelaya-forks/feature/fix-set-empty-max-visits
Fixed maxVisits being set to 0 when trying to reset it
2020-01-28 18:51:18 +01:00
Alejandro Celaya
43cd9722a9 Updated project to node 12.14.1 2020-01-28 18:40:33 +01:00
Alejandro Celaya
f3154e770e Fixed maxVisits being set to 0 when trying to reset it 2020-01-28 18:36:23 +01:00
Alejandro Celaya
44aca4aeda Merge pull request #192 from acelaya-forks/feature/version-constraints
Feature/version constraints
2020-01-19 21:37:56 +01:00
Alejandro Celaya
5762342d6c Ensured edit meta menu item is only displayed when shlink v1.18 or greater is run 2020-01-19 21:30:01 +01:00
Alejandro Celaya
2236ed467e Ensured date range filtering is only displayed if Shlink v1.21 ow higer is run 2020-01-19 21:25:45 +01:00
Alejandro Celaya
d244b830ac Updated date on license file 2020-01-19 21:20:32 +01:00
Alejandro Celaya
e89b68fe1e Merge pull request #190 from acelaya-forks/feature/edit-meta
Feature/edit meta
2020-01-19 21:15:25 +01:00
Alejandro Celaya
1f588c5b13 Updated changelog with v2.3.0 2020-01-19 21:00:31 +01:00
Alejandro Celaya
38cad143a0 Created EditMetaModal test 2020-01-19 20:59:01 +01:00
Alejandro Celaya
f52bcc5389 Ensured state is reset on edit meta modal after closing it 2020-01-19 20:37:12 +01:00
Alejandro Celaya
caa6f7bcd8 Created shortUrlMetaReducer test 2020-01-19 20:21:59 +01:00
Alejandro Celaya
207a8cef20 Updated tests from modified code 2020-01-19 13:20:46 +01:00
Alejandro Celaya
d44a4b260e Finished component to allow metadata to be edited on existing short URLs 2020-01-19 13:07:33 +01:00
Alejandro Celaya
80a8e0b55c Created component to edit short URLs meta 2020-01-17 21:07:59 +01:00
Alejandro Celaya
2d60f830f7 Improved icons on short URL menu 2020-01-15 20:25:58 +01:00
Alejandro Celaya
90751a09f7 Merge pull request #188 from acelaya-forks/feature/visits-amount
Feature/visits amount
2020-01-15 18:42:09 +01:00
Alejandro Celaya
301da4bb2a Recovered behavior to show amount of visits in selected date range on visits detail page 2020-01-15 18:31:28 +01:00
Alejandro Celaya
c90cd46095 Removed old ExternalLink component in favor of external one 2020-01-15 18:16:12 +01:00
Alejandro Celaya
7826000384 Merge pull request #187 from acelaya-forks/feature/date-filter
Feature/date filter
2020-01-14 20:59:01 +01:00
Alejandro Celaya
b48dcdd5e1 Fixed wrong files being picked for mutations 2020-01-14 20:48:01 +01:00
Alejandro Celaya
4f6326b139 Updated changelog 2020-01-14 20:21:14 +01:00
Alejandro Celaya
cff96eeccc Created DateRangeRow test 2020-01-14 20:20:27 +01:00
Alejandro Celaya
5eb4a3adec Fixed tests and typos 2020-01-14 20:12:30 +01:00
Alejandro Celaya
b60908a5e9 Added filtering by date to short URLs list 2020-01-14 19:59:25 +01:00
Alejandro Celaya
124441238b Moved style to the proper scope 2020-01-12 12:08:26 +01:00
Alejandro Celaya
4ec0287a74 Merge pull request #185 from Starbix/patch-2
Update nginx base image
2020-01-12 11:01:38 +01:00
Cédric Laubacher
05c67a5c99 Update nginx base image 2020-01-12 10:50:40 +01:00
Alejandro Celaya
f507a3628c Merge pull request #184 from acelaya-forks/feature/show-max-visits
Feature/show max visits
2020-01-11 20:23:02 +01:00
Alejandro Celaya
89e9d2b2d1 Fixed accidentally refactored string 2020-01-11 20:11:41 +01:00
Alejandro Celaya
595858ac4b Used visits count component in short URL visits view 2020-01-11 20:10:12 +01:00
Alejandro Celaya
3f2162fe62 Extracted visits count component to reuse it in other places 2020-01-11 19:58:04 +01:00
Alejandro Celaya
f2cb30409a Updated changelog 2020-01-11 19:41:41 +01:00
Alejandro Celaya
5c4fec5a2f Displayed amount of max visits on those URLs which have it 2020-01-11 19:40:16 +01:00
Alejandro Celaya
e96c119432 Merge pull request #183 from acelaya-forks/feature/support-shlink-2
Feature/support shlink 2
2020-01-11 14:25:25 +01:00
Alejandro Celaya
0920962d72 Used already unpacked property 2020-01-11 14:16:23 +01:00
Alejandro Celaya
aaeb0fff78 Updated changelog 2020-01-11 14:13:58 +01:00
Alejandro Celaya
de41f50945 Ensured preview menu item is hidden when consuming Shlink 2 2020-01-11 14:12:58 +01:00
Alejandro Celaya
0f51bf95e3 Updated ShlinkApiClient so that it retries API version when v2 is not supported 2020-01-11 13:55:37 +01:00
Alejandro Celaya
ba8cade6fc When handling API errors, use the type prop and fallback to error if not found 2020-01-11 12:24:45 +01:00
Alejandro Celaya
dbefae5a01 Merge pull request #173 from Starbix/patch-1
Update baseimages
2019-11-21 11:35:49 +01:00
Cédric Laubacher
727b219742 Update nginx image to latest version 2019-11-21 07:55:40 +01:00
Alejandro Celaya
fb25e44b58 Used strict version number for nginx base image 2019-11-21 07:52:50 +01:00
Cédric Laubacher
fe2d394831 Update baseimages
Nginx can be set to the latest patch version, as its API is really stable.
2019-11-16 10:24:34 +01:00
Alejandro Celaya
efd08ff1d6 Updated changelog 2019-11-10 13:04:15 +01:00
Alejandro Celaya
4b861a5376 Removed profanity 2019-11-10 08:47:13 +01:00
Alejandro Celaya
2076e7d5e8 Merge pull request #171 from MartinH0/master
Updated FavIcons
2019-11-10 08:44:59 +01:00
MartinH0
37f6f1f90c rename to favicon 2019-11-09 22:34:41 +01:00
MartinH0
81f76e0bd6 SVG FavIcon Fix
Added sizes="any" to the svg FavIcon
2019-11-09 22:32:13 +01:00
MartinH0
69b305cd8a Added SVG FavIcon
Added SVG FavIcon as File in Rootdirectory
2019-11-09 22:29:37 +01:00
MartinH0
45742a066e Added SVG FavIcon 2019-11-09 22:28:44 +01:00
MartinH0
86fb8b3f7c Added FavIcons
Added .gif and .png FavIcon
2019-11-09 19:38:50 +01:00
MartinH0
9c0fc8e1d2 Adjusted to FavIcons
Added MS Icons, Added all Apple Icons, Added all normal Icons and standard FavIcons
2019-11-09 19:37:31 +01:00
MartinH0
10d6302180 Added all FavIcons to Manifest
Added all missing FavIcons to the manifest
2019-11-09 19:22:55 +01:00
MartinH0
da7ed6992f FavIcon
Overwrite FavIcon with 128x128 new optimized FavIcon
2019-11-09 19:17:45 +01:00
MartinH0
32c9375ac8 FavIcons edits
Added new FavIcons and overwrite old ones
2019-11-09 19:16:12 +01:00
MartinH0
7ed1334a51 Updated FavIcons
Added all Versions of all available FavIcons.
Also Added normal FavIcons additionally to the apple ones.
Also added the "msapplication-TileImage" meta for Microsoft.
2019-11-07 21:47:57 +01:00
Alejandro Celaya
d9097896f6 Added github funding 2019-10-22 19:33:59 +02:00
491 changed files with 34768 additions and 14388 deletions

View File

@@ -1,6 +1,7 @@
./.github
./build
./coverage
./dist
./node_modules
./test
./shlink-web-client.gif
./dist

View File

@@ -1,43 +1,17 @@
{
"extends": [
"adidas-env/browser",
"adidas-env/module",
"adidas-env/node",
"adidas-es6",
"adidas-babel",
"adidas-react"
"@shlinkio/js-coding-standard"
],
"plugins": ["jest"],
"env": {
"jest/globals": true
},
"parserOptions": {
"tsconfigRootDir": ".",
"createDefaultProgram": true
},
"globals": {
"process": true,
"setImmediate": true
},
"settings": {
"react": {
"version": "16.3"
}
},
"rules": {
"comma-dangle": ["error", "always-multiline"],
"no-invalid-this": "off",
"no-console": "warn",
"template-curly-spacing": ["error", "never"],
"no-warning-comments": "off",
"no-magic-numbers": "off",
"no-undefined": "off",
"indent": ["error", 2, {
"SwitchCase": 1
}
],
"react/jsx-curly-spacing": ["error", "never"],
"react/jsx-indent-props": ["error", 2],
"react/jsx-first-prop-new-line": ["error", "multiline-multiprop"],
"react/jsx-closing-bracket-location": ["error", "tag-aligned"],
"react/no-array-index-key": "off",
"react/no-did-update-set-state": "off",
"react/display-name": "off"
}
}

1
.github/FUNDING.yml vendored
View File

@@ -1 +1,2 @@
github: ['acelaya']
custom: ['https://acel.me/donate']

View File

@@ -1,6 +1,7 @@
<!--
Before opening an issue, just take into account that this is a completely free of charge open source project.
I'm always happy to help and provide support, but some understanding will be required.
Before opening an issue, just take into account that this is a completely free of charge and open source project.
I'm always happy to help and provide support, but some understanding will be expected.
I do this in my own free time, so expect some delays when implementing new features and fixing bugs, and don't take it personal if an issue gets eventually closed.
You may also be asked to provide tests or ways to reproduce reported bugs.
Try to be polite, and understand it is impossible for an OSS project to cover all use cases.
-->

View File

@@ -5,9 +5,10 @@ labels: bug
---
<!--
Before opening an issue, just take into account that this is a completely free of charge open source project.
I'm always happy to help and provide support, but some understanding will be required.
Before opening an issue, just take into account that this is a completely free of charge and open source project.
I'm always happy to help and provide support, but some understanding will be expected.
I do this in my own free time, so expect some delays when implementing new features and fixing bugs, and don't take it personal if an issue gets eventually closed.
You may also be asked to provide tests or ways to reproduce reported bugs.
Try to be polite, and understand it is impossible for an OSS project to cover all use cases.
With that said, please fill in the information requested next. More information might be requested once the issue is open.

View File

@@ -5,9 +5,10 @@ labels: feature
---
<!--
Before opening an issue, just take into account that this is a completely free of charge open source project.
I'm always happy to help and provide support, but some understanding will be required.
Before opening an issue, just take into account that this is a completely free of charge and open source project.
I'm always happy to help and provide support, but some understanding will be expected.
I do this in my own free time, so expect some delays when implementing new features and fixing bugs, and don't take it personal if an issue gets eventually closed.
You may also be asked to provide tests or ways to reproduce reported bugs.
Try to be polite, and understand it is impossible for an OSS project to cover all use cases.
With that said, please fill in the information requested next. More information might be requested once the issue is open.

View File

@@ -5,9 +5,10 @@ labels: question
---
<!--
Before opening an issue, just take into account that this is a completely free of charge open source project.
I'm always happy to help and provide support, but some understanding will be required.
Before opening an issue, just take into account that this is a completely free of charge and open source project.
I'm always happy to help and provide support, but some understanding will be expected.
I do this in my own free time, so expect some delays when implementing new features and fixing bugs, and don't take it personal if an issue gets eventually closed.
You may also be asked to provide tests or ways to reproduce reported bugs.
Try to be polite, and understand it is impossible for an OSS project to cover all use cases.
With that said, please fill in the information requested next. More information might be requested once the issue is open.

58
.github/workflows/ci.yml vendored Normal file
View File

@@ -0,0 +1,58 @@
name: Continuous integration
on:
pull_request: null
push:
branches:
- main
jobs:
lint:
runs-on: ubuntu-20.04
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Use node.js 14.15
uses: actions/setup-node@v1
with:
node-version: 14.15
- run: npm ci
- run: npm run lint
unit-tests:
runs-on: ubuntu-20.04
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Use node.js 14.15
uses: actions/setup-node@v1
with:
node-version: 14.15
- run: npm ci
- run: npm run test:ci
- name: Publish coverage
uses: codecov/codecov-action@v1
with:
file: ./coverage/clover.xml
mutation-tests:
continue-on-error: true
runs-on: ubuntu-20.04
steps:
- name: Checkout code
uses: actions/checkout@v2
with:
fetch-depth: 0 # needed so that the main branch is also fetched
- name: Use node.js 14.15
uses: actions/setup-node@v1
with:
node-version: 14.15
- run: npm ci
- run: npm run mutate -- --mutate=$(git diff origin/main --name-only | grep -E 'src\/(.*).(ts|tsx)$' | paste -sd ",")
build-docker-image:
runs-on: ubuntu-20.04
steps:
- name: Checkout code
uses: actions/checkout@v2
- run: docker build -t shlink-web-client:test .

View File

@@ -0,0 +1,28 @@
name: Build docker image
on:
push:
branches:
- main
tags:
- 'v*'
jobs:
build:
runs-on: ubuntu-20.04
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
with:
version: latest
- name: Login to docker hub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
- name: Build the image
run: bash ./scripts/docker/build

28
.github/workflows/publish-release.yml vendored Normal file
View File

@@ -0,0 +1,28 @@
name: Publish release
on:
push:
tags:
- 'v*'
jobs:
build:
runs-on: ubuntu-20.04
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Use node.js 14.15
uses: actions/setup-node@v1
with:
node-version: 14.15
- name: Generate release assets
run: npm ci && npm run build ${GITHUB_REF#refs/tags/v}
- name: Publish release with assets
uses: docker://antonyurchenko/git-release:latest
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ALLOW_TAG_PREFIX: "true"
ALLOW_EMPTY_CHANGELOG: "true"
with:
args: |
dist/shlink-web-client_*_dist.zip

View File

@@ -1,6 +0,0 @@
build:
environment:
node: v12.11.0
tools:
external_code_coverage:
timeout: 1200

View File

@@ -1,40 +0,0 @@
language: node_js
node_js:
- "12.11.0"
cache:
directories:
- node_modules
services:
- docker
install:
- npm ci
before_script:
- echo "Building commit range ${TRAVIS_COMMIT_RANGE}"
- export MUTATION_FILES=$(git diff ${TRAVIS_COMMIT_RANGE:-origin/master} --name-only | grep -E 'src\/(.*).(js|ts|jsx|tsx)' | paste -sd ",")
script:
- npm run lint
- npm run test:ci
- if [[ -z $TRAVIS_TAG ]]; then docker build -t shlink-web-client:test . ; fi
- if [[ -z $TRAVIS_TAG ]]; then npm run mutate:ci ; fi
after_success:
- node_modules/.bin/ocular coverage/clover.xml
# Before deploying, build dist file for current travis tag
before_deploy:
- npm run build ${TRAVIS_TAG#?}
deploy:
provider: releases
api_key:
secure: jBvPwC7EAbViaNR83rwMSt5XQDK0Iu9rgvEMa7GoyShbHcvUCCPd73Tu9quNpKi6NKsDY3INHgtch3vgonjGNGDGJ+yDyIBzXcvsAX2x3UcHpRbgY12uiINVmQxBI1+OVQB016Nm+cKC/i5Z36K4EmDbYfo+MrKndngM6AjcQFTwI8EwniIMaQgg4gNes//K8NhP5u0c3gwG+Q6jEGnq6uH3kcRgh6/epIZYpQyxjqWqKwF77sgcYj+X2Nf6XxtB5neuCi301UKLoLx8G0skh/Lm6KAIO4s9iIhIFa3UpoF21Ka0TxLpd2JxalLryCnFGlWWE6lxC9Htmc0TeRowJQlGdJXCskJ37xT9MljKY0fwNMu06VS/FUgykuCv+jP3zQu51pKu7Ew7+WeNPjautoOTu54VkdGyHcf2ThBNEyJQuiEwAQe4u7yAxY6R5ovEdvHBSIg4w1E5/Mxy5SMTCUlIAv6H7QQ1X9Z/zJm9HH5KeKz5tsHvQ/RIdSpgHXq/tC8o4Yup/LCFucXfrgvy/8pJoO1UpOlmvm62974NFfo0EG5YWwv6brUqz3QXpMjb8sWqgjltYMYJX3J7WZ34rIc+zt4NAmfhqgczaOC4pUGCiJ8jX3rMWIaQRn1AJ+5V337jL9fNDpTHny4phQjHrMJ1e0HZuNp0Xb5Q8wgqDPM=
file: "./dist/shlink-web-client_${TRAVIS_TAG#?}_dist.zip"
skip_cleanup: true
on:
tags: true

View File

@@ -4,358 +4,570 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org).
## 2.2.2 - 2019-10-21
#### Added
## [3.1.1] - 2021-05-08
### Added
* *Nothing*
#### Changed
### Changed
* *Nothing*
#### Deprecated
### Deprecated
* *Nothing*
#### Removed
### Removed
* *Nothing*
#### Fixed
### Fixed
* [#413](https://github.com/shlinkio/shlink-web-client/issues/413) Fixed edit short URL form reflecting outdated info after navigating back from other section.
* [#412](https://github.com/shlinkio/shlink-web-client/issues/412) Ensured new visits coming from mercure hub are prepended and not appended, to keep proper sorting.
* [#417](https://github.com/shlinkio/shlink-web-client/issues/417) Fixed link spanning out of QR code modal.
* [#411](https://github.com/shlinkio/shlink-web-client/issues/411) Added missing feedback when editing a short URL to know if everything went right.
## [3.1.0] - 2021-03-29
### Added
* [#379](https://github.com/shlinkio/shlink-web-client/issues/379) and [#384](https://github.com/shlinkio/shlink-web-client/issues/384) Improved QR code modal, including controls to customize size, format and margin, as well as a button to copy the link to the clipboard.
* [#385](https://github.com/shlinkio/shlink-web-client/issues/385) Added setting to determine if "validate URL" should be enabled or disabled by default.
* [#386](https://github.com/shlinkio/shlink-web-client/issues/386) Added new card in overview section to display amount of orphan visits when using Shlink 2.6.0 or higher.
* [#177](https://github.com/shlinkio/shlink-web-client/issues/177) Added dark theme.
* [#387](https://github.com/shlinkio/shlink-web-client/issues/387) and [#395](https://github.com/shlinkio/shlink-web-client/issues/395) Added a section to see orphan visits stats, when consuming Shlink >=2.6.0.
* [#383](https://github.com/shlinkio/shlink-web-client/issues/383) Added title to short URLs list, displayed when consuming Shlink >=2.6.0.
* [#368](https://github.com/shlinkio/shlink-web-client/issues/368) Added new settings to define the default interval for visits pages.
* [#349](https://github.com/shlinkio/shlink-web-client/issues/349) Added support to export visits to CSV.
* [#397](https://github.com/shlinkio/shlink-web-client/issues/397) New section to edit all data for short URLs, including title when using Shlink v2.6 or newer.
This new section replaces the old modals to edit short URL meta, short URL tags and the long URL. Everything is now together in the same section.
### Changed
* [#382](https://github.com/shlinkio/shlink-web-client/issues/382) Ensured short URL tags are edited through the `PATCH /short-urls/{shortCode}` endpoint when using Shlink 2.6.0 or higher.
* [#398](https://github.com/shlinkio/shlink-web-client/issues/398) Improved performance when loading short URL details by avoiding API calls if the short URL is already present in local state.
### Deprecated
* *Nothing*
### Removed
* *Nothing*
### Fixed
* [#335](https://github.com/shlinkio/shlink-web-client/issues/335) Fixed linting errors.
## [3.0.1] - 2020-12-30
### Added
* *Nothing*
### Changed
* *Nothing*
### Deprecated
* *Nothing*
### Removed
* *Nothing*
### Fixed
* [#364](https://github.com/shlinkio/shlink-web-client/issues/364) Fixed all dropdowns so that they are consistently styled.
* [#366](https://github.com/shlinkio/shlink-web-client/issues/366) Fixed text in visits menu jumping to next line in some tablet resolutions.
* [#367](https://github.com/shlinkio/shlink-web-client/issues/367) Removed conflicting overflow in visits table for mobile devices.
* [#365](https://github.com/shlinkio/shlink-web-client/issues/365) Fixed weird rendering of short URLs list in tablets.
* [#372](https://github.com/shlinkio/shlink-web-client/issues/372) Fixed importing servers in Android devices.
## [3.0.0] - 2020-12-22
### Added
* [#340](https://github.com/shlinkio/shlink-web-client/issues/340) Added new "overview" page, showing basic information of the active server.
As a side effect, it also introduces improvements in the "create short URL" page, grouping components by context and explaining what they are for.
* [#309](https://github.com/shlinkio/shlink-web-client/issues/309) Added new domain selector component in create URL form which allows selecting from previously used domains or set a new one.
* [#315](https://github.com/shlinkio/shlink-web-client/issues/315) Now you can tell if you want to validate the long URL when using Shlink >=2.4.
* [#285](https://github.com/shlinkio/shlink-web-client/issues/285) Improved visits section:
* Charts are now grouped in tabs, so that only one part of the components is rendered at a time.
* Amount of highlighted visits is now displayed.
* Date filtering can be now selected through relative times (last 7 days, last 30 days, etc) or absolute dates using date pickers.
* Only the visits for last 30 days are loaded by default. You can change that at any moment if required.
* [#355](https://github.com/shlinkio/shlink-web-client/issues/355) Improved home page, fixing also its scrolling behavior for mobile devices.
### Changed
* [#267](https://github.com/shlinkio/shlink-web-client/issues/267) Added some subtle but important improvements on UI/UX.
* [#352](https://github.com/shlinkio/shlink-web-client/issues/352) Moved from Scrutinizer to Codecov as the code coverage backend.
* [#217](https://github.com/shlinkio/shlink-web-client/issues/217) Improved how messages are displayed, by centralizing it in the `Message` and `Result` components.
* [#219](https://github.com/shlinkio/shlink-web-client/issues/219) Improved error messages when something fails while interacting with Shlink's API.
### Deprecated
* *Nothing*
### Removed
* [#344](https://github.com/shlinkio/shlink-web-client/issues/344) Dropped support for Shlink v1.
### Fixed
* *Nothing*
## [2.6.2] - 2020-11-14
### Added
* *Nothing*
### Changed
* [#325](https://github.com/shlinkio/shlink-web-client/issues/325) and [#294](https://github.com/shlinkio/shlink-web-client/issues/294) Updated all dependencies, including React 17, Typescript 4, react-datepicker 3 and Stryker 4.
### Deprecated
* *Nothing*
### Removed
* *Nothing*
### Fixed
* [#334](https://github.com/shlinkio/shlink-web-client/issues/334) Fixed color-picker making the app crash when closing the modal without closing the color-picker, and then trying to open the modal again.
* [#333](https://github.com/shlinkio/shlink-web-client/issues/333) Fixed visits getting accumulated every time the visits page is opened.
## [2.6.1] - 2020-10-31
### Added
* *Nothing*
### Changed
* [#292](https://github.com/shlinkio/shlink-web-client/issues/292) Improved a bit how caching works by removing the service worker and adding proper HTTP caching config on nginx inside docker image.
### Deprecated
* *Nothing*
### Removed
* *Nothing*
### Fixed
* [#316](https://github.com/shlinkio/shlink-web-client/issues/316) Fixed manifest.json file not getting downloaded after passing credentials when the app is protected with basic auth.
* [#311](https://github.com/shlinkio/shlink-web-client/issues/311) Fixed datepicker showing below other components.
* [#306](https://github.com/shlinkio/shlink-web-client/issues/306) Fixed multi-arch docker builds by replacing node-sass with dart-sass.
* [#328](https://github.com/shlinkio/shlink-web-client/issues/328) Fixed toggle switches getting broken in mobile resolutions.
## [2.6.0] - 2020-09-20
### Added
* [#289](https://github.com/shlinkio/shlink-web-client/issues/289) Client and server version constraints are now links to the corresponding project release notes.
* [#293](https://github.com/shlinkio/shlink-web-client/issues/293) Shlink versions are now always displayed in footer, hiding the server version when there's no connected server.
* [#250](https://github.com/shlinkio/shlink-web-client/issues/250) Added support to group real time updates in fixed intervals.
The settings page now allows to provide the interval in which the UI should get updated, making that happen at once, with all the updates that have happened during that interval.
By default updates are immediately applied if real-time updates are enabled, to keep the behavior as it was.
* [#277](https://github.com/shlinkio/shlink-web-client/issues/277) Added highlighting capabilities to the visits line chart.
### Changed
* [#150](https://github.com/shlinkio/shlink-web-client/issues/150) The list of short URLs is now ordered by the creation date, showing newest results first.
* [#248](https://github.com/shlinkio/shlink-web-client/issues/248) Numbers displayed application-wide are now prettified.
* [#40](https://github.com/shlinkio/shlink-web-client/issues/40) Migrated project to TypeScript.
* [#297](https://github.com/shlinkio/shlink-web-client/issues/297) Moved docker image building to github actions.
* [#305](https://github.com/shlinkio/shlink-web-client/issues/305) Split travis build so that every step is run in a parallel job.
### Deprecated
* *Nothing*
### Removed
* *Nothing*
### Fixed
* [#295](https://github.com/shlinkio/shlink-web-client/issues/295) Fixed custom slug field not being disabled when selecting a short code length.
* [#301](https://github.com/shlinkio/shlink-web-client/issues/301) Fixed tags visits loading not being cancelled when leaving visits page.
## [2.5.1] - 2020-06-06
### Added
* *Nothing*
### Changed
* [#254](https://github.com/shlinkio/shlink-web-client/issues/254) Reduced duplication on code to handle mercure topics binding.
### Deprecated
* *Nothing*
### Removed
* *Nothing*
### Fixed
* [#276](https://github.com/shlinkio/shlink-web-client/issues/276) Fixed default grouping used for visits line chart, making it be dynamic depending on how old the short URL is.
* [#280](https://github.com/shlinkio/shlink-web-client/issues/280) Fixed shlink-web-client version not being properly passed when building stable tags of the docker image.
* [#269](https://github.com/shlinkio/shlink-web-client/issues/269) Fixed doughnut chart legends getting to big and hiding charts on mobile devices.
## [2.5.0] - 2020-05-31
### Added
* [#148](https://github.com/shlinkio/shlink-web-client/issues/148) Added support for real-time updates when consuming a Shlink version that is integrated with a mercure hub server.
The integration is transparent. When a server is opened, shlink-web-client will try to get the mercure info from it.
* If it works, it will setup the necessary `EventSource`s, dispatching redux actions when an event is pushed, which will in turn update the UI.
* If it fails, it will assume it is either not configured or not supported by the Shlink version.
* [#265](https://github.com/shlinkio/shlink-web-client/issues/265) Updated tags section to allow displaying number of short URLs using every tag and number of visits for all short URLs using the tag.
This will work only when using Shlink v2.2.0 or above. For previous versions, the tags page will continue behaving the same.
* [#261](https://github.com/shlinkio/shlink-web-client/issues/261) Added new page to show visit stats by tag.
This new page will return a "not found" error when the server is lower than v2.2.0, as older versions do not support fetching stats by tag.
* [#253](https://github.com/shlinkio/shlink-web-client/issues/253) Created new settings page that will be used to define customizations in the app.
* [#149](https://github.com/shlinkio/shlink-web-client/issues/149) and [#198](https://github.com/shlinkio/shlink-web-client/issues/198) Added new line chart to visits and tags stats which displays amount of visits during selected time period, grouped by month, week, day or hour.
### Changed
* [#218](https://github.com/shlinkio/shlink-web-client/issues/218) Added back button to sections not displayed in left menu.
* [#255](https://github.com/shlinkio/shlink-web-client/issues/255) Improved how servers and settings are persisted in the local storage.
### Deprecated
* *Nothing*
### Removed
* *Nothing*
### Fixed
* [#262](https://github.com/shlinkio/shlink-web-client/issues/262) Fixed charts displaying decimal numbers, when visits are absolute and that makes no sense.
## [2.4.0] - 2020-04-10
### Added
* [#199](https://github.com/shlinkio/shlink-web-client/issues/199) Added table to visits page which displays the information in a paginated, sortable and filterable list.
It also supports selecting multiple visits in the table which makes the corresponding data to be highlighted in the visits charts.
* [#241](https://github.com/shlinkio/shlink-web-client/issues/241) Added support to select charts bars in order to highlight related stats in other charts.
It also selects the visits in the new table, and you can even combine a selection in the chart and in the table.
* [#213](https://github.com/shlinkio/shlink-web-client/issues/213) The versions of both shlink-web-client and currently consumed Shlink server are now displayed in the footer.
* [#221](https://github.com/shlinkio/shlink-web-client/issues/221) Improved how servers are handled, displaying meaningful errors when a not-found or a not-reachable server is tried to be loaded.
* [#226](https://github.com/shlinkio/shlink-web-client/issues/226) Created servers can now be edited.
* [#234](https://github.com/shlinkio/shlink-web-client/issues/234) Allowed short code length to be edited on any new short URL when using Shlink 2.1 or higher.
* [#235](https://github.com/shlinkio/shlink-web-client/issues/235) Allowed editing the long URL for any existing short URL when suing Shlink 2.1 or higher.
### Changed
* [#205](https://github.com/shlinkio/shlink-web-client/issues/205) Replaced `jest-each` package by jet's native `test.each` function.
* [#209](https://github.com/shlinkio/shlink-web-client/issues/209) Replaced `Unknown` by `Direct` for visits from undetermined referrers.
* [#212](https://github.com/shlinkio/shlink-web-client/issues/212) Moved copy-to-clipboard next to short URL.
* [#208](https://github.com/shlinkio/shlink-web-client/issues/208) Short URLs list paginator is now progressive.
### Deprecated
* *Nothing*
### Removed
* *Nothing*
### Fixed
* [#243](https://github.com/shlinkio/shlink-web-client/issues/243) Fixed loading state and resetting on short URL creation form.
* [#239](https://github.com/shlinkio/shlink-web-client/issues/239) Fixed how user agents are parsed, reducing false results.
## [2.3.1] - 2020-02-08
### Added
* *Nothing*
### Changed
* [#191](https://github.com/shlinkio/shlink-web-client/issues/191) Created `ForServerVersion` helper component which dynamically renders children if current server conditions are met.
* [#189](https://github.com/shlinkio/shlink-web-client/issues/189) Simplified short url tags and short url deletion components and reducers, by removing redundant actions.
### Deprecated
* *Nothing*
### Removed
* *Nothing*
### Fixed
* [#193](https://github.com/shlinkio/shlink-web-client/issues/193) Fixed `maxVisits` being set to 0 when trying to reset it from having a value to `null`.
* [#196](https://github.com/shlinkio/shlink-web-client/issues/196) Included apache `.htaccess` file which takes care of falling back to index.html when reloading the page on a client-side handled route.
* [#179](https://github.com/shlinkio/shlink-web-client/issues/179) Ensured domain is provided to Shlink server when editing, deleting or fetching short URLs which do not belong to default domain.
* [#202](https://github.com/shlinkio/shlink-web-client/issues/202) Fixed domain not passed when dispatching actions that affect a single short URL (edit tags, edit meta and delete), which cased the list not to be properly updated.
## [2.3.0] - 2020-01-19
### Added
* [#174](https://github.com/shlinkio/shlink-web-client/issues/174) Added complete support for Shlink v2.x together with currently supported Shlink versions.
* [#164](https://github.com/shlinkio/shlink-web-client/issues/164) Added max visits control on those URLs which have `maxVisits`.
* [#178](https://github.com/shlinkio/shlink-web-client/issues/178) Short URLs list can now be filtered by date range.
* [#46](https://github.com/shlinkio/shlink-web-client/issues/46) Allowed short URL's metadata to be edited (`maxVisits`, `validSince` and `validUntil`).
### Changed
* *Nothing*
### Deprecated
* *Nothing*
### Removed
* *Nothing*
### Fixed
* [#170](https://github.com/shlinkio/shlink-web-client/issues/170) Fixed apple icon referencing to incorrect file names.
## [2.2.2] - 2019-10-21
### Added
* *Nothing*
### Changed
* *Nothing*
### Deprecated
* *Nothing*
### Removed
* *Nothing*
### Fixed
* [#167](https://github.com/shlinkio/shlink-web-client/issues/167) Fixed `/servers.json` path not being ignored when returning something other than an array.
## 2.2.1 - 2019-10-18
#### Added
## [2.2.1] - 2019-10-18
### Added
* *Nothing*
#### Changed
### Changed
* *Nothing*
#### Deprecated
### Deprecated
* *Nothing*
#### Removed
### Removed
* *Nothing*
#### Fixed
### Fixed
* [#165](https://github.com/shlinkio/shlink-web-client/issues/165) Fixed error thrown when opening "create" page while using a Shlink version which does not return a valid SemVer version (like `latest` docker image, or any development instance).
## 2.2.0 - 2019-10-05
#### Added
## [2.2.0] - 2019-10-05
### Added
* [#144](https://github.com/shlinkio/shlink-web-client/issues/144) Added domain input to create domain page.
#### Changed
### Changed
* [#140](https://github.com/shlinkio/shlink-web-client/issues/140) Updated project dependencies.
#### Deprecated
### Deprecated
* *Nothing*
#### Removed
### Removed
* *Nothing*
#### Fixed
### Fixed
* *Nothing*
## 2.1.1 - 2019-09-22
#### Added
## [2.1.1] - 2019-09-22
### Added
* *Nothing*
#### Changed
### Changed
* [#142](https://github.com/shlinkio/shlink-web-client/issues/142) Updated to newer versions of base docker images for dev and production.
#### Deprecated
### Deprecated
* *Nothing*
#### Removed
### Removed
* *Nothing*
#### Fixed
### Fixed
* [#151](https://github.com/shlinkio/shlink-web-client/issues/151) Fixed "order by" indicator (caret) still indicate ASC on column header when no order is specified.
* [#157](https://github.com/shlinkio/shlink-web-client/issues/157) Fixed pagination control on graphs expanding too much when lots of pages need to be rendered.
* [#155](https://github.com/shlinkio/shlink-web-client/issues/155) Fixed client-side paths resolve to 404 when served from nginx in docker image instead of falling back to `index.html`.
## 2.1.0 - 2019-05-19
#### Added
## [2.1.0] - 2019-05-19
### Added
* [#101](https://github.com/shlinkio/shlink-web-client/issues/101) Added checkbox to short URL creation form that allows to determine the value of the `findIfExists` flag introduced in Shlink v1.16.0.
* [#105](https://github.com/shlinkio/shlink-web-client/issues/105) Added support to pre-configure servers. See [how to pre-configure servers](README.md#pre-configuring-servers) to get more details on how to do it.
#### Changed
### Changed
* [#125](https://github.com/shlinkio/shlink-web-client/issues/125) Refactored reducers to replace `switch` statements by `handleActions` from [redux-actions](https://github.com/redux-utilities/redux-actions).
* [#116](https://github.com/shlinkio/shlink-web-client/issues/116) Removed sinon in favor of jest mocks.
* [#72](https://github.com/shlinkio/shlink-web-client/issues/72) Increased code coverage up to 80%.
#### Deprecated
### Deprecated
* *Nothing*
#### Removed
### Removed
* *Nothing*
#### Fixed
### Fixed
* *Nothing*
## 2.0.3 - 2019-03-16
#### Added
## [2.0.3] - 2019-03-16
### Added
* *Nothing*
#### Changed
### Changed
* *Nothing*
#### Deprecated
### Deprecated
* *Nothing*
#### Removed
### Removed
* *Nothing*
#### Fixed
### Fixed
* [#120](https://github.com/shlinkio/shlink-web-client/issues/120) Fixed crash when visits page is loaded and there are no visits with known cities.
* [#113](https://github.com/shlinkio/shlink-web-client/issues/113) Ensured visits loading is cancelled when the visits page is unmounted. Requests on flight will still finish.
* [#118](https://github.com/shlinkio/shlink-web-client/issues/118) Fixed chart crashing when trying to render lots of bars by adding pagination.
## 2.0.2 - 2019-03-04
#### Added
## [2.0.2] - 2019-03-04
### Added
* *Nothing*
#### Changed
### Changed
* *Nothing*
#### Deprecated
### Deprecated
* *Nothing*
#### Removed
### Removed
* *Nothing*
#### Fixed
### Fixed
* [#103](https://github.com/shlinkio/shlink-web-client/issues/103) Fixed visits page getting freezed when loading large amounts of visits.
* [#111](https://github.com/shlinkio/shlink-web-client/issues/111) Fixed crash when trying to load a map modal with only one location.
* [#115](https://github.com/shlinkio/shlink-web-client/issues/115) Created `ErrorHandler` component which will prevent crashes in app to make it unusable.
## 2.0.1 - 2019-03-03
#### Added
## [2.0.1] - 2019-03-03
### Added
* *Nothing*
#### Changed
### Changed
* [#106](https://github.com/shlinkio/shlink-web-client/issues/106) Reduced size of docker image by using a multi-stage build Dockerfile.
* [#95](https://github.com/shlinkio/shlink-web-client/issues/95) Tested docker image build during travis executions.
#### Deprecated
### Deprecated
* *Nothing*
#### Removed
### Removed
* *Nothing*
#### Fixed
### Fixed
* [#104](https://github.com/shlinkio/shlink-web-client/issues/104) Fixed blank page being showed when not-found paths are loaded.
* [#94](https://github.com/shlinkio/shlink-web-client/issues/94) Fixed initial zoom and center on maps.
* [#93](https://github.com/shlinkio/shlink-web-client/issues/93) Prevented side menu to be swipeable while a modal window is displayed.
## 2.0.0 - 2019-01-13
#### Added
## [2.0.0] - 2019-01-13
### Added
* [#54](https://github.com/shlinkio/shlink-web-client/issues/54) Added stats by city graphic in visits page.
* [#55](https://github.com/shlinkio/shlink-web-client/issues/55) Added map in visits page locating cities from which visits have occurred.
#### Changed
### Changed
* [#87](https://github.com/shlinkio/shlink-web-client/issues/87) and [#89](https://github.com/shlinkio/shlink-web-client/issues/89) Updated all dependencies to latest major versions.
* [#96](https://github.com/shlinkio/shlink-web-client/issues/96) Updated visits page to load visits in multiple paginated requests of `5000` visits when used shlink server supports it. This will prevent shlink to hang when trying to load big amounts of visits.
* [#71](https://github.com/shlinkio/shlink-web-client/issues/71) Improved tests and increased code coverage.
#### Deprecated
### Deprecated
* *Nothing*
#### Removed
### Removed
* [#59](https://github.com/shlinkio/shlink-web-client/issues/59) Dropped support for old browsers. Internet explorer and dead browsers are no longer supported.
* [#97](https://github.com/shlinkio/shlink-web-client/issues/97) Dropped support for authentication via `Authorization` header with Bearer type and JWT, which will make this version no longer work with shlink earlier than v1.13.0.
#### Fixed
### Fixed
* *Nothing*
## 1.2.1 - 2018-12-21
#### Added
## [1.2.1] - 2018-12-21
### Added
* *Nothing*
#### Changed
### Changed
* [#80](https://github.com/shlinkio/shlink-web-client/issues/80) Deeply refactored app to do true dependency injection with an IoC container.
* [#79](https://github.com/shlinkio/shlink-web-client/issues/79) Updated to nginx 1.15.7 as the base docker image.
* [#75](https://github.com/shlinkio/shlink-web-client/issues/75) Prevented duplicated `yarn build` in travis when a tag exists.
#### Deprecated
### Deprecated
* *Nothing*
#### Removed
### Removed
* *Nothing*
#### Fixed
### Fixed
* [#77](https://github.com/shlinkio/shlink-web-client/issues/77) Sortable graphs ordering is now case insensitive.
## 1.2.0 - 2018-11-01
#### Added
## [1.2.0] - 2018-11-01
### Added
* [#65](https://github.com/shlinkio/shlink-web-client/issues/65) Added sorting to both countries and referrers stats graphs.
* [#14](https://github.com/shlinkio/shlink-web-client/issues/14) Documented how to build the project so that it can be served from a subpath.
#### Changed
### Changed
* [#50](https://github.com/shlinkio/shlink-web-client/issues/50) Improved tests and increased code coverage.
#### Deprecated
### Deprecated
* *Nothing*
#### Removed
### Removed
* *Nothing*
#### Fixed
### Fixed
* [#66](https://github.com/shlinkio/shlink-web-client/issues/66) Fixed tooltips in graphs with too small bars not being displayed.
## 1.1.1 - 2018-10-20
#### Added
## [1.1.1] - 2018-10-20
### Added
* [#57](https://github.com/shlinkio/shlink-web-client/issues/57) Automated release generation in travis build.
#### Changed
### Changed
* *Nothing*
#### Deprecated
### Deprecated
* *Nothing*
#### Removed
### Removed
* *Nothing*
#### Fixed
### Fixed
* [#63](https://github.com/shlinkio/shlink-web-client/issues/63) Improved how bar charts are rendered in stats page, making them try to calculate a bigger height for big data sets.
* [#56](https://github.com/shlinkio/shlink-web-client/issues/56) Ensured `ColorGenerator` matches keys in a case insensitive way.
* [#53](https://github.com/shlinkio/shlink-web-client/issues/53) Fixed missing margin between date fields in visits page for mobile devices.
## 1.1.0 - 2018-09-16
#### Added
## [1.1.0] - 2018-09-16
### Added
* [#47](https://github.com/shlinkio/shlink-web-client/issues/47) Added support to delete short URLs (requires [shlink v1.12.0](https://github.com/shlinkio/shlink/releases/tag/v1.12.0) or greater).
#### Changed
### Changed
* [#35](https://github.com/shlinkio/shlink-web-client/issues/35) Visits component split into two, which makes the header not to be refreshed when filtering by date, and also the visits global counter now reflects the actual number of visits which fulfill current filter.
* [#36](https://github.com/shlinkio/shlink-web-client/issues/36) Tags selector now autocompletes existing tag names, to prevent typos and ease reusing existing tags.
* [#39](https://github.com/shlinkio/shlink-web-client/issues/39) Defined `propTypes` as static properties in class components.
#### Deprecated
### Deprecated
* *Nothing*
#### Removed
### Removed
* *Nothing*
#### Fixed
### Fixed
* [#49](https://github.com/shlinkio/shlink-web-client/issues/49) Ensured filtering parameters are reseted when list component is unmounted so that params are not mixed when coming back.
* [#45](https://github.com/shlinkio/shlink-web-client/issues/45) Ensured graphs x-axis start at `0` and don't use decimals.
* [#51](https://github.com/shlinkio/shlink-web-client/issues/51) When editing short URL tags, the value returned form server is used when refreshing the list, which is normalized.
## 1.0.1 - 2018-09-02
#### Added
## [1.0.1] - 2018-09-02
### Added
* *Nothing*
#### Changed
### Changed
* *Nothing*
#### Deprecated
### Deprecated
* *Nothing*
#### Removed
### Removed
* *Nothing*
#### Fixed
### Fixed
* [#42](https://github.com/shlinkio/shlink-web-client/issues/42) Fixed selected tags lost when navigating between pages in short URLs list.
* [#43](https://github.com/shlinkio/shlink-web-client/issues/43) Fixed "List short URLs" menu item only selected when in first page.
## 1.0.0 - 2018-08-26
#### Added
## [1.0.0] - 2018-08-26
### Added
* [#4](https://github.com/shlinkio/shlink-web-client/issues/4) Now it is possible to export and import servers.
* Export all servers in a CSV file.
@@ -372,69 +584,53 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
* [#22](https://github.com/shlinkio/shlink-web-client/issues/22) Improved code coverage.
* [#28](https://github.com/shlinkio/shlink-web-client/issues/28) Added integration with [Scrutinizer](https://scrutinizer-ci.com/g/shlinkio/shlink-web-client/).
#### Changed
### Changed
* [#33](https://github.com/shlinkio/shlink-web-client/issues/33) Changed to [adidas coding style](https://github.com/adidas/js-linter-configs) for Javascript.
* [#32](https://github.com/shlinkio/shlink-web-client/issues/32) Changed to [adidas coding style](https://github.com/adidas/js-linter-configs) for stylesheets.
* [#26](https://github.com/shlinkio/shlink-web-client/issues/26) The tags input now displays tags using their actual color.
#### Deprecated
### Deprecated
* *Nothing*
#### Removed
### Removed
* *Nothing*
#### Fixed
### Fixed
* *Nothing*
## 0.2.0 - 2018-08-12
#### Added
## [0.2.0] - 2018-08-12
### Added
* [#12](https://github.com/shlinkio/shlink-web-client/issues/12) Improved code coverage
* [#20](https://github.com/shlinkio/shlink-web-client/issues/20) Added servers list in welcome page, as well as added link to create one when none exist.
#### Changed
### Changed
* [#11](https://github.com/shlinkio/shlink-web-client/issues/11) Improved app icons fro progressive web apps.
#### Deprecated
### Deprecated
* *Nothing*
#### Removed
### Removed
* *Nothing*
#### Fixed
### Fixed
* [#19](https://github.com/shlinkio/shlink-web-client/issues/19) Added workaround in tags input so that it is possible to add tags on Android devices.
* [#17](https://github.com/shlinkio/shlink-web-client/issues/17) Fixed short URLs list not being sortable in mobile resolutions.
* [#13](https://github.com/shlinkio/shlink-web-client/issues/13) Improved visits page on mobile resolutions.
## 0.1.1 - 2018-08-06
#### Added
## [0.1.1] - 2018-08-06
### Added
* [#15](https://github.com/shlinkio/shlink-web-client/issues/15) Added a `Dockerfile` that can be used to generate a distributable docker image
#### Changed
### Changed
* *Nothing*
#### Deprecated
### Deprecated
* *Nothing*
#### Removed
### Removed
* *Nothing*
#### Fixed
### Fixed
* *Nothing*

72
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,72 @@
# Contributing
This file will guide you through the process of getting to project up and running, in case you want to provide coding contributions.
You will also see how to ensure the code fulfills the expected code checks, and how to create a pull request.
## System dependencies
The project can be run inside a docker container through provided docker-compose configuration.
Because of this, the only actual dependencies are [docker](https://docs.docker.com/get-docker/) and [docker-compose](https://docs.docker.com/compose/install/).
## Setting up the project
The first thing you need to do is fork the repository, and clone it in your local machine.
Then you will have to follow these steps:
* Copy the file `docker-compose.override.yml.dist` by also removing the `dist` extension.
* Start-up the project by running `docker-compose up`.
Once this is finished, you will have the project exposed in port `3000` (http://localhost:3000).
## Project structure
This project is a [react](https://reactjs.org/) & [redux](https://redux.js.org/) application, built with [typescript](https://www.typescriptlang.org/), which is distributed as a 100% client-side progressive web application.
This is the basic project structure:
```
shlink-web-client
├── config
├── public
├── scripts
├── src
├── test
├── package.json
└── README.md
```
* `config`: It contains some configuration scripts, used during testing, linting and building of the project.
* `public`: Will act as the application document root once built, and contains some static assets (favicons, images, etc).
* `scripts`: It has some of the CLI scripts used to run tests or building.
* `src`: Contains the main source code of the application, including both web components, SASS stylesheets and files with logic.
* `test`: Contains the project tests.
## Running code checks
> Note: The `indocker` shell script is a helper used to run commands inside the docker container.
* `./indocker npm run lint`: Checks coding styles are fulfilled, both in JS/TS files as well as in stylesheets.
* `./indocker npm run lint:js`: Checks coding styles are fulfilled in JS/TS files.
* `./indocker npm run lint:css`: Checks coding styles are fulfilled in stylesheets.
* `./indocker npm run lint:js:fix`: Fixes coding styles in JS/TS files.
* `./indocker npm run lint:css:fix`: Fixes coding styles in stylesheets.
* `./indocker npm run test`: Runs unit tests with Jest.
* `./indocker npm run mutate`: Runs mutation tests with StrykerJS (this command can be very slow).
## Building the project
The source code in this project cannot be run directly in a web browser, you need to build it first.
* `./indocker npm run build`: Builds the project using a combination of `webpack`, `babel` and `tsc`, generating the final static files. The content is placed in the `build` folder, which is automatically created if it does not exist.
* `./indocker npm run serve:build`: Serves the static files inside the `build` folder in port 5000 (http://localhost:5000). Useful to test the content built with previous command.
## Pull request process
In order to provide pull requests to this project, you should always start by creating a new branch, where you will make all desired changes.
The base branch should always be `main`, and the target branch for the pull request should also be `main`.
Before your branch can be merged, all the checks described in [Running code checks](#running-code-checks) have to be passing. You can verify that manually, or wait for the build to be run automatically after the pull request is created.

View File

@@ -1,8 +1,11 @@
FROM node:12.11.0-alpine as node
FROM node:14.15-alpine as node
COPY . /shlink-web-client
RUN cd /shlink-web-client && npm install && npm run build
ARG VERSION="latest"
ENV VERSION ${VERSION}
RUN cd /shlink-web-client && \
npm install && npm run build -- ${VERSION} --no-dist
FROM nginx:1.17.4-alpine
FROM nginx:1.19.6-alpine
LABEL maintainer="Alejandro Celaya <alejandro@alejandrocelaya.com>"
RUN rm -r /usr/share/nginx/html && rm /etc/nginx/conf.d/default.conf
COPY config/docker/nginx.conf /etc/nginx/conf.d/default.conf

View File

@@ -1,6 +1,6 @@
MIT License
Copyright (c) 2018-2019 shlinkio
Copyright (c) 2018-2020 shlinkio
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@@ -1,36 +1,46 @@
# shlink-web-client
[![Build Status](https://img.shields.io/travis/shlinkio/shlink-web-client.svg?style=flat-square)](https://travis-ci.org/shlinkio/shlink-web-client)
[![Docker build status](https://img.shields.io/docker/cloud/build/shlinkio/shlink-web-client.svg?style=flat-square)](https://hub.docker.com/r/shlinkio/shlink-web-client/)
[![Code Coverage](https://img.shields.io/scrutinizer/coverage/g/shlinkio/shlink-web-client.svg?style=flat-square)](https://scrutinizer-ci.com/g/shlinkio/shlink-web-client/?branch=master)
[![Scrutinizer Code Quality](https://img.shields.io/scrutinizer/g/shlinkio/shlink-web-client.svg?style=flat-square)](https://scrutinizer-ci.com/g/shlinkio/shlink-web-client/?branch=master)
[![Build Status](https://img.shields.io/github/workflow/status/shlinkio/shlink-web-client/Continuous%20integration/main?logo=github&style=flat-square)](https://github.com/shlinkio/shlink-web-client/actions?query=workflow%3A%22Continuous+integration%22)
[![Code Coverage](https://img.shields.io/codecov/c/gh/shlinkio/shlink-web-client/main?style=flat-square)](https://app.codecov.io/gh/shlinkio/shlink-web-client)
[![GitHub release](https://img.shields.io/github/release/shlinkio/shlink-web-client.svg?style=flat-square)](https://github.com/shlinkio/shlink-web-client/releases/latest)
[![GitHub license](https://img.shields.io/github/license/shlinkio/shlink-web-client.svg?style=flat-square)](https://github.com/shlinkio/shlink-web-client/blob/master/LICENSE)
[![Paypal Donate](https://img.shields.io/badge/Donate-paypal-blue.svg?style=flat-square&logo=paypal&colorA=cccccc)](https://acel.me/donate)
[![Docker pulls](https://img.shields.io/docker/pulls/shlinkio/shlink-web-client.svg?logo=docker&style=flat-square)](https://hub.docker.com/r/shlinkio/shlink-web-client/)
[![GitHub license](https://img.shields.io/github/license/shlinkio/shlink-web-client.svg?style=flat-square)](https://github.com/shlinkio/shlink-web-client/blob/main/LICENSE)
[![Paypal Donate](https://img.shields.io/badge/Donate-paypal-blue.svg?style=flat-square&logo=paypal&colorA=cccccc)](https://slnk.to/donate)
A ReactJS-based progressive web application for [Shlink](https://shlink.io).
![shlink-web-client](shlink-web-client.gif)
> If you are trying to find out how to run the project in development mode or how to provide contributions, read the [CONTRIBUTING](CONTRIBUTING.md) doc.
## Installation
There are three ways in which you can use this application.
* The easiest way to use shlink-web-client is by just going to <https://app.shlink.io>.
### From app.shlink.io
The application runs 100% in the browser, so you can safely access any shlink instance from there.
The easiest way to use shlink-web-client is by just going to <https://app.shlink.io>.
* Self hosting the application yourself.
The application runs 100% in the browser, so you can safely access any shlink instance from there.
Get the [latest release](https://github.com/shlinkio/shlink-web-client/releases/latest) and download the distributable zip file attached to it (`shlink-web-client_X.X.X_dist.zip`).
### Docker image
The package contains static files only, so just put it in a folder and serve it with the web server of your choice.
If you want to deploy shlink-web-client in a container-based cluster (kubernetes, docker swarm, etc), just pick the [shlinkio/shlink-web-client](https://hub.docker.com/r/shlinkio/shlink-web-client/) image and do it.
Provided dist files are configured to be served from the root of your domain. If you need to serve shlink-web-client from a subpath, you will have to build it yourself following [these steps](#serve-shlink-in-subpath).
It's a lightweight [nginx:alpine](https://hub.docker.com/r/library/nginx/) image serving the static app on port 80.
* Using the official [docker image](https://hub.docker.com/r/shlinkio/shlink-web-client/)
### Self-hosted
If you want to deploy shlink-web-client in a container-based cluster (kubernetes, docker swarm, etc), just pick the `shlinkio/shlink-web-client` image and do it.
If you want to self-host it yourself, get the [latest release](https://github.com/shlinkio/shlink-web-client/releases/latest) and download the distributable zip file attached to it (`shlink-web-client_X.X.X_dist.zip`).
It's a lightweight [nginx:alpine](https://hub.docker.com/r/library/nginx/) image serving the static app on port 80.
The package contains static files only, so just put it in a folder and serve it with the web server of your choice.
**Considerations**:
* Provided dist files are configured to be served from the root of your domain. If you need to serve shlink-web-client from a subpath, you will have to build it yourself following [these steps](#serve-shlink-in-subpath).
* The app has a client-side router that handles dynamic paths. Because of that, you need to configure your web server to fall-back to the `index.html` file when requested files do not exist.
* If you use Apache, you are covered, since the project includes an `.htaccess` file which already does this.
* If you use nginx, you can [see how it's done](config/docker/nginx.conf) for the docker image and do the same.
## Pre-configuring servers
@@ -59,6 +69,14 @@ If you are using the shlink-web-client docker image, you can mount the `servers.
docker run --name shlink-web-client -p 8000:80 -v ${PWD}/servers.json:/usr/share/nginx/html/servers.json shlinkio/shlink-web-client
> **Be extremely careful when using this feature.**
>
> Due to shlink-web-client's client-side nature, the file needs to be accessible from the browser.
>
> Because of that, make sure you use this only when you self-host shlink-web-client, and you know only trusted people will have access to it.
>
> Failing to do this could cause your API keys to end up being exposed.
## Serve project in subpath
Official distributable files have been built so that they are served from the root of a domain.

View File

@@ -4,11 +4,26 @@ server {
root /usr/share/nginx/html;
index index.html;
# When requesting static paths with extension, try them, and return a 404 if not found
location ~ .+\.(css|js|html|png|jpg|jpeg|gif|bmp|ico|json|csv|otf|eot|svg|svgz|ttf|woff|woff2|ijmap|pdf|tif|map) {
try_files $uri $uri/ =404;
# Expire rules for static content
# HTML files should never be cached. There's only one here, which is the entry point (index.html)
location ~* \.(?:manifest|appcache|html?|xml|json)$ {
expires -1;
}
# Images and other binary assets can be saved for a month
location ~* \.(?:jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc)$ {
expires 1M;
add_header Cache-Control "public";
}
# JS and CSS files can be saved for a year, as they are always hashed. New versions will include a new hash anyway, forcing the download
location ~* \.(?:css|js)$ {
expires 1y;
add_header Cache-Control "public";
}
# When requesting static paths with extension, try them, and return a 404 if not found
location ~* .+\.(css|js|html|png|jpe?g|gif|bmp|ico|json|csv|otf|eot|svg|svgz|ttf|woff|woff2|ijmap|pdf|tif|map) {
try_files $uri $uri/ =404;
}
# When requesting a path without extension, try it, and return the index if not found
# This allows HTML5 history paths to be handled by the client application
location / {

View File

@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/prefer-nullish-coalescing, @typescript-eslint/promise-function-async, @typescript-eslint/prefer-optional-chain */
const fs = require('fs');
const path = require('path');
@@ -10,7 +11,7 @@ const { NODE_ENV } = process.env;
if (!NODE_ENV) {
throw new Error(
'The NODE_ENV environment variable is required but was not specified.'
'The NODE_ENV environment variable is required but was not specified.',
);
}
@@ -36,7 +37,7 @@ dotenvFiles.forEach((dotenvFile) => {
require('dotenv-expand')(
require('dotenv').config({
path: dotenvFile,
})
}),
);
}
});
@@ -82,7 +83,7 @@ function getClientEnvironment(publicUrl) {
// This should only be used as an escape hatch. Normally you would put
// images into the `src` and `import` them in code to get their paths.
PUBLIC_URL: publicUrl,
}
},
);
// Stringify all values so we can feed into Webpack DefinePlugin

View File

@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/prefer-nullish-coalescing, @typescript-eslint/promise-function-async, @typescript-eslint/prefer-optional-chain */
const path = require('path');
const fs = require('fs');

View File

@@ -1,4 +1,4 @@
import Enzyme from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import Adapter from '@wojtekmaj/enzyme-adapter-react-17';
Enzyme.configure({ adapter: new Adapter() });

View File

@@ -13,7 +13,6 @@ const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const safePostCssParser = require('postcss-safe-parser');
const ManifestPlugin = require('webpack-manifest-plugin');
const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
const WorkboxWebpackPlugin = require('workbox-webpack-plugin');
const WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin');
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');
const getCSSModuleLocalIdent = require('react-dev-utils/getCSSModuleLocalIdent');
@@ -75,7 +74,7 @@ module.exports = (webpackEnv) => {
loader: MiniCssExtractPlugin.loader,
options: Object.assign(
{},
shouldUseRelativeAssetPaths ? { publicPath: '../../' } : undefined
shouldUseRelativeAssetPaths ? { publicPath: '../../' } : undefined,
),
},
{
@@ -227,7 +226,7 @@ module.exports = (webpackEnv) => {
// Turned on because emoji and regex is not minified properly using default
// https://github.com/facebook/create-react-app/issues/2488
ascii_only: true,
ascii_only: true, // eslint-disable-line @typescript-eslint/camelcase
},
},
@@ -281,7 +280,7 @@ module.exports = (webpackEnv) => {
modules: [ 'node_modules' ].concat(
// It is guaranteed to exist because we tweak it in `env.js`
process.env.NODE_PATH.split(path.delimiter).filter(Boolean)
process.env.NODE_PATH.split(path.delimiter).filter(Boolean),
),
// These are the reasonable defaults supported by the Node ecosystem.
@@ -372,7 +371,7 @@ module.exports = (webpackEnv) => {
loader: require.resolve('babel-loader'),
options: {
customize: require.resolve(
'babel-preset-react-app/webpack-overrides'
'babel-preset-react-app/webpack-overrides',
),
plugins: [
@@ -470,7 +469,7 @@ module.exports = (webpackEnv) => {
importLoaders: 2,
sourceMap: isEnvProduction && shouldUseSourceMap,
},
'sass-loader'
'sass-loader',
),
// Don't consider CSS imports dead code even if the
@@ -491,7 +490,7 @@ module.exports = (webpackEnv) => {
modules: true,
getLocalIdent: getCSSModuleLocalIdent,
},
'sass-loader'
'sass-loader',
),
},
@@ -544,8 +543,8 @@ module.exports = (webpackEnv) => {
minifyURLs: true,
},
}
: undefined
)
: undefined,
),
),
// Inlines the webpack runtime script. This script is too small to warrant
@@ -611,25 +610,6 @@ module.exports = (webpackEnv) => {
// You can remove this if you don't use Moment.js:
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
// Generate a service worker script that will precache, and keep up to date,
// the HTML & assets that are part of the Webpack build.
isEnvProduction &&
new WorkboxWebpackPlugin.GenerateSW({
clientsClaim: true,
exclude: [ /\.map$/, /asset-manifest\.json$/ ],
importWorkboxFrom: 'cdn',
navigateFallback: `${publicUrl}/index.html`,
navigateFallbackBlacklist: [
// Exclude URLs starting with /_, as they're likely an API call
new RegExp('^/_'),
// Exclude URLs containing a dot, as they're likely a resource in
// public/ and not a SPA route
new RegExp('/[^/]+\\.[^/]+$'),
],
}),
// TypeScript type checking
useTypeScript &&
new ForkTsCheckerWebpackPlugin({
@@ -668,7 +648,7 @@ module.exports = (webpackEnv) => {
fs: 'empty',
net: 'empty',
tls: 'empty',
child_process: 'empty',
child_process: 'empty', // eslint-disable-line @typescript-eslint/camelcase
},
// Turn off performance processing because we utilize

View File

@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/prefer-nullish-coalescing */
const fs = require('fs');
const errorOverlayMiddleware = require('react-dev-utils/errorOverlayMiddleware');
@@ -109,7 +110,7 @@ module.exports = function(proxy, allowedHost) {
// We do this in development to avoid hitting the production cache if
// it used the same host and port.
// https://github.com/facebook/create-react-app/issues/2272#issuecomment-302832432
app.use(noopServiceWorkerMiddleware());
app.use(noopServiceWorkerMiddleware(paths.publicUrl));
},
};
};

View File

@@ -3,7 +3,7 @@ version: '3'
services:
shlink_web_client_node:
container_name: shlink_web_client_node
image: node:12.11.0-alpine
image: node:14.15-alpine
command: /bin/sh -c "cd /home/shlink/www && npm install && npm run start"
volumes:
- ./:/home/shlink/www

View File

@@ -1,12 +1,12 @@
module.exports = {
coverageDirectory: '<rootDir>/coverage',
collectCoverageFrom: [
'src/**/*.js',
'src/**/*.{js,ts,tsx}',
'!src/registerServiceWorker.js',
'!src/index.js',
'!src/reducers/index.js',
'!src/**/provideServices.js',
'!src/container/*.js',
'!src/index.ts',
'!src/reducers/index.ts',
'!src/**/provideServices.ts',
'!src/container/*.ts',
],
resolver: 'jest-pnp-resolver',
setupFiles: [
@@ -17,9 +17,9 @@ module.exports = {
testEnvironment: 'jsdom',
testURL: 'http://localhost',
transform: {
'^.+\\.(js|jsx|mjs)$': '<rootDir>/node_modules/babel-jest',
'^.+\\.(ts|tsx|js|jsx|mjs)$': '<rootDir>/node_modules/babel-jest',
'^.+\\.css$': '<rootDir>/config/jest/cssTransform.js',
'^(?!.*\\.(js|jsx|mjs|css|json)$)': '<rootDir>/config/jest/fileTransform.js',
'^(?!.*\\.(ts|tsx|js|jsx|mjs|css|json)$)': '<rootDir>/config/jest/fileTransform.js',
},
transformIgnorePatterns: [
'[/\\\\]node_modules[/\\\\].+\\.(js|jsx|ts|tsx)$',

25104
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,148 +1,165 @@
{
"name": "shlink-web-client",
"description": "A React-based progressive web application for shlink",
"version": "2.3.0",
"private": false,
"homepage": "",
"repository": "https://github.com/shlinkio/shlink-web-client",
"license": "MIT",
"scripts": {
"lint": "npm run lint:js && npm run lint:css",
"lint:js": "eslint src test scripts config",
"lint": "npm run lint:css && npm run lint:js",
"lint:js": "eslint --ext .js,.ts,.tsx src test",
"lint:js:fix": "npm run lint:js -- --fix",
"lint:css": "stylelint src/*.scss src/**/*.scss",
"lint:css:fix": "npm run lint:css -- --fix",
"start": "node scripts/start.js",
"serve:build": "serve ./build",
"build": "node scripts/build.js",
"test": "node scripts/test.js --env=jsdom --colors",
"test": "node scripts/test.js --env=jsdom --colors --verbose",
"test:ci": "npm run test -- --coverage --coverageReporters=text --coverageReporters=text-summary --coverageReporters=clover",
"test:pretty": "npm run test -- --coverage --coverageReporters=text --coverageReporters=text-summary --coverageReporters=html",
"mutate": "./node_modules/.bin/stryker run",
"mutate:ci": "npm run mutate -- --mutate=$MUTATION_FILES"
"mutate": "./node_modules/.bin/stryker run --concurrency 4"
},
"dependencies": {
"@fortawesome/fontawesome-free": "^5.11.2",
"@fortawesome/fontawesome-svg-core": "^1.2.25",
"@fortawesome/free-regular-svg-icons": "^5.11.2",
"@fortawesome/free-solid-svg-icons": "^5.11.2",
"@fortawesome/react-fontawesome": "^0.1.5",
"array-filter": "^1.0.0",
"array-map": "^0.0.0",
"array-reduce": "^0.0.0",
"axios": "^0.19.0",
"bootstrap": "^4.3.1",
"bottlejs": "^1.7.2",
"chart.js": "^2.8.0",
"@fortawesome/fontawesome-free": "^5.15.2",
"@fortawesome/fontawesome-svg-core": "^1.2.34",
"@fortawesome/free-regular-svg-icons": "^5.15.2",
"@fortawesome/free-solid-svg-icons": "^5.15.2",
"@fortawesome/react-fontawesome": "^0.1.14",
"axios": "^0.21.1",
"bootstrap": "^4.6.0",
"bottlejs": "^2.0.0",
"bowser": "^2.11.0",
"chart.js": "^2.9.4",
"classnames": "^2.2.6",
"compare-versions": "^3.5.1",
"compare-versions": "^3.6.0",
"csvjson": "^5.1.0",
"leaflet": "^1.5.1",
"moment": "^2.24.0",
"promise": "^8.0.3",
"prop-types": "^15.7.2",
"qs": "^6.9.0",
"ramda": "^0.26.1",
"react": "^16.10.2",
"react-autosuggest": "^9.4.3",
"react-chartjs-2": "^2.8.0",
"react-color": "^2.17.3",
"react-copy-to-clipboard": "^5.0.1",
"react-datepicker": "~1.5.0",
"react-dom": "^16.10.2",
"react-external-link": "^1.0.0",
"react-leaflet": "^2.4.0",
"react-moment": "^0.9.5",
"react-redux": "^7.1.1",
"react-router-dom": "^5.1.2",
"react-swipeable": "^5.4.0",
"event-source-polyfill": "^1.0.22",
"leaflet": "^1.7.1",
"moment": "^2.29.1",
"promise": "^8.1.0",
"qs": "^6.9.6",
"ramda": "^0.27.1",
"react": "^17.0.1",
"react-autosuggest": "^10.1.0",
"react-chartjs-2": "^2.11.1",
"react-color": "^2.19.3",
"react-copy-to-clipboard": "^5.0.2",
"react-datepicker": "^3.6.0",
"react-dom": "^17.0.1",
"react-external-link": "^1.2.0",
"react-leaflet": "^3.1.0",
"react-moment": "^1.0.0",
"react-redux": "^7.2.2",
"react-router-dom": "^5.2.0",
"react-swipeable": "^6.0.1",
"react-tagsinput": "^3.19.0",
"reactstrap": "^8.0.1",
"redux": "^4.0.4",
"redux-actions": "^2.6.5",
"reactstrap": "^8.9.0",
"redux": "^4.0.5",
"redux-localstorage-simple": "^2.4.0",
"redux-thunk": "^2.3.0",
"uuid": "^3.3.3"
"uuid": "^8.3.2"
},
"devDependencies": {
"@babel/core": "^7.6.2",
"@stryker-mutator/core": "^2.1.0",
"@stryker-mutator/html-reporter": "^2.1.0",
"@stryker-mutator/javascript-mutator": "^2.1.0",
"@stryker-mutator/jest-runner": "^2.1.0",
"@svgr/webpack": "^4.3.3",
"adm-zip": "^0.4.13",
"autoprefixer": "^9.6.3",
"@babel/core": "^7.13.8",
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.13.8",
"@babel/plugin-proposal-optional-chaining": "^7.13.8",
"@shlinkio/eslint-config-js-coding-standard": "~1.2.2",
"@stryker-mutator/core": "^4.4.1",
"@stryker-mutator/jest-runner": "^4.4.1",
"@stryker-mutator/typescript-checker": "^4.4.1",
"@svgr/webpack": "^5.5.0",
"@types/chart.js": "^2.9.31",
"@types/classnames": "^2.2.11",
"@types/enzyme": "^3.10.8",
"@types/jest": "^26.0.20",
"@types/leaflet": "^1.5.23",
"@types/moment": "^2.13.0",
"@types/qs": "^6.9.5",
"@types/ramda": "^0.27.38",
"@types/react": "^17.0.2",
"@types/react-autosuggest": "^10.1.2",
"@types/react-color": "^3.0.4",
"@types/react-copy-to-clipboard": "^5.0.0",
"@types/react-datepicker": "^3.1.5",
"@types/react-dom": "^17.0.1",
"@types/react-leaflet": "^2.5.2",
"@types/react-redux": "^7.1.16",
"@types/react-router-dom": "^5.1.7",
"@types/react-tagsinput": "^3.19.7",
"@types/uuid": "^8.3.0",
"@wojtekmaj/enzyme-adapter-react-17": "^0.3.1",
"adm-zip": "^0.4.16",
"autoprefixer": "^10.0.2",
"babel-core": "7.0.0-bridge.0",
"babel-eslint": "^10.0.3",
"babel-jest": "^24.9.0",
"babel-loader": "^8.0.6",
"babel-plugin-named-asset-import": "^0.3.4",
"babel-preset-react-app": "^9.0.2",
"babel-jest": "^26.6.3",
"babel-loader": "^8.2.1",
"babel-plugin-named-asset-import": "^0.3.7",
"babel-preset-react-app": "^10.0.0",
"babel-runtime": "^6.26.0",
"bfj": "^7.0.1",
"case-sensitive-paths-webpack-plugin": "^2.2.0",
"chalk": "^2.4.2",
"css-loader": "^3.2.0",
"dotenv": "^8.1.0",
"bfj": "^7.0.2",
"case-sensitive-paths-webpack-plugin": "^2.3.0",
"chalk": "^4.1.0",
"css-loader": "^5.0.1",
"dart-sass": "^1.25.0",
"dotenv": "^8.2.0",
"dotenv-expand": "^5.1.0",
"enzyme": "^3.10.0",
"enzyme-adapter-react-16": "^1.14.0",
"eslint": "^5.11.1",
"eslint-config-adidas-babel": "^1.1.0",
"eslint-config-adidas-env": "^1.1.0",
"eslint-config-adidas-es6": "^1.2.0",
"eslint-config-adidas-react": "^1.1.1",
"eslint-loader": "^3.0.2",
"eslint-plugin-import": "^2.18.2",
"eslint-plugin-jest": "^22.17.0",
"eslint-plugin-jsx-a11y": "^6.2.3",
"eslint-plugin-promise": "^4.2.1",
"eslint-plugin-react": "^7.16.0",
"file-loader": "^4.2.0",
"enzyme": "^3.11.0",
"eslint": "^7.13.0",
"eslint-loader": "^4.0.2",
"file-loader": "^6.2.0",
"fork-ts-checker-webpack-plugin-alt": "^0.4.14",
"fs-extra": "^8.1.0",
"html-webpack-plugin": "^4.0.0-beta.8",
"fs-extra": "^9.0.1",
"html-webpack-plugin": "^4.5.0",
"identity-obj-proxy": "^3.0.0",
"jest": "^24.9.0",
"jest-each": "^24.9.0",
"jest-pnp-resolver": "^1.2.1",
"jest-resolve": "^24.9.0",
"mini-css-extract-plugin": "^0.8.0",
"node-sass": "^4.12.0",
"jest": "^26.6.3",
"jest-pnp-resolver": "^1.2.2",
"jest-resolve": "^26.6.2",
"mini-css-extract-plugin": "^1.3.1",
"object-assign": "^4.1.1",
"ocular.js": "^0.1.0",
"optimize-css-assets-webpack-plugin": "^5.0.3",
"pnp-webpack-plugin": "^1.5.0",
"postcss": "^7.0.18",
"postcss-flexbugs-fixes": "^4.1.0",
"optimize-css-assets-webpack-plugin": "^5.0.4",
"pnp-webpack-plugin": "^1.6.4",
"postcss": "^8.1.7",
"postcss-flexbugs-fixes": "^4.2.1",
"postcss-loader": "^3.0.0",
"postcss-preset-env": "^6.7.0",
"postcss-safe-parser": "^4.0.1",
"postcss-safe-parser": "^5.0.2",
"raf": "^3.4.1",
"react-app-polyfill": "^1.0.4",
"react-dev-utils": "^9.1.0",
"resolve": "^1.12.0",
"sass-loader": "^8.0.0",
"serve": "^11.2.0",
"react-app-polyfill": "^2.0.0",
"react-dev-utils": "^11.0.0",
"resolve": "^1.19.0",
"sass": "^1.29.0",
"sass-loader": "^10.1.0",
"serve": "^11.3.2",
"stryker-cli": "^1.0.0",
"style-loader": "^1.0.0",
"stylelint": "^9.10.1",
"stylelint-config-adidas": "^1.2.1",
"style-loader": "^2.0.0",
"stylelint": "^13.7.2",
"stylelint-config-adidas": "^1.3.0",
"stylelint-config-adidas-bem": "^1.2.0",
"stylelint-config-recommended-scss": "^4.0.0",
"stylelint-scss": "^3.11.1",
"sw-precache-webpack-plugin": "^0.11.5",
"terser-webpack-plugin": "^2.1.2",
"url-loader": "^2.2.0",
"webpack": "^4.41.0",
"webpack-dev-server": "^3.8.2",
"stylelint-config-recommended-scss": "^4.2.0",
"stylelint-scss": "^3.18.0",
"sw-precache-webpack-plugin": "^1.0.0",
"terser-webpack-plugin": "^4.2.3",
"ts-jest": "^26.5.2",
"ts-mockery": "^1.2.0",
"typescript": "^4.2.2",
"url-loader": "^4.1.1",
"webpack": "^4.44.2",
"webpack-dev-server": "^3.11.0",
"webpack-manifest-plugin": "^2.2.0",
"whatwg-fetch": "^3.0.0",
"workbox-webpack-plugin": "^4.3.1"
"whatwg-fetch": "^3.5.0"
},
"babel": {
"presets": [
"react-app"
[
"react-app",
{
"runtime": "automatic"
}
]
],
"plugins": [
"@babel/plugin-proposal-optional-chaining",
"@babel/plugin-proposal-nullish-coalescing-operator"
]
},
"browserslist": [

16
public/.htaccess Normal file
View File

@@ -0,0 +1,16 @@
RewriteEngine on
RewriteBase /
# do not do anything for already existing files
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule (.*) - [L]
# if request is no valid file NOR directory
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
# if static asset do not do anything
RewriteRule (.*)(css|js|html|png|jpe?g|gif|bmp|ico|json|csv|otf|eot|svg|svgz|ttf|woff|woff2|ijmap|pdf|tif|map) - [NC,L,R=404]
# everything else should be redirected to /index.html so it can be routed by it
RewriteRule (.*) /index.html [L]

BIN
public/favicon.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 642 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
public/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

1
public/favicon.svg Normal file
View File

@@ -0,0 +1 @@
<svg width="512pt" height="512pt" viewBox="0 0 512 512" version="1.1" xmlns="http://www.w3.org/2000/svg"><g fill="#4595e3"><path d=" M 23.71 85.08 C 17.22 49.81 49.44 14.86 85.08 18.12 C 118.83 19.21 145.72 53.33 139.45 86.37 C 155.64 102.30 171.32 118.83 187.87 134.36 C 198.32 111.73 208.84 89.12 219.57 66.62 C 226.05 53.84 243.47 48.74 255.73 56.27 C 263.76 62.10 270.34 69.69 277.25 76.75 C 286.28 86.61 285.72 102.89 276.31 112.31 C 223.38 165.37 170.38 218.37 117.35 271.34 C 107.72 280.99 91.01 281.25 81.11 271.86 C 74.39 264.94 66.82 258.69 61.24 250.77 C 53.72 238.52 58.85 221.07 71.64 214.62 C 94.11 203.87 116.72 193.38 139.33 182.91 C 123.81 166.36 107.30 150.68 91.37 134.49 C 60.20 140.28 27.37 116.78 23.71 85.08 Z" /><path d=" M 205.21 201.23 C 225.32 181.36 260.88 181.11 281.14 200.86 C 299.25 218.75 317.37 236.65 335.10 254.93 C 356.73 278.01 352.01 318.70 326.03 336.56 C 320.07 330.47 313.73 324.65 308.12 318.28 C 323.86 309.39 328.76 286.18 316.63 272.39 C 301.73 256.95 286.30 242.03 271.24 226.75 C 264.49 219.65 256.80 212.00 246.37 211.52 C 224.65 208.64 205.52 233.36 214.49 253.58 C 221.09 266.81 234.22 275.12 243.62 286.24 C 240.43 295.96 238.09 306.13 238.29 316.46 C 225.55 304.29 213.16 291.73 200.89 279.09 C 180.97 257.57 183.10 220.45 205.21 201.23 Z" /><path d=" M 273.90 352.07 C 252.28 328.99 256.98 288.31 282.96 270.46 C 288.93 276.54 295.26 282.36 300.88 288.72 C 285.14 297.62 280.23 320.82 292.38 334.61 C 307.27 350.05 322.70 364.96 337.75 380.25 C 344.51 387.35 352.20 395.00 362.64 395.48 C 384.35 398.37 403.49 373.64 394.51 353.42 C 387.92 340.18 374.78 331.88 365.38 320.76 C 368.56 311.04 370.91 300.86 370.71 290.54 C 383.45 302.70 395.84 315.27 408.11 327.91 C 428.03 349.43 425.90 386.55 403.78 405.77 C 383.68 425.64 348.13 425.89 327.86 406.14 C 309.75 388.25 291.60 370.37 273.90 352.07 Z" /><path d=" M 422.11 403.83 C 431.96 394.07 441.60 384.06 451.66 374.51 C 460.90 383.74 471.89 392.70 474.89 406.11 C 480.16 429.97 484.08 454.13 488.76 478.12 C 490.00 483.41 484.47 488.29 479.35 486.63 C 454.66 481.52 429.55 478.12 405.14 471.84 C 393.17 467.97 385.20 457.75 376.55 449.27 C 386.39 439.49 396.13 429.60 406.06 419.91 C 416.37 433.45 435.74 414.00 422.11 403.83 Z" /></g></svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
public/icons/icon-16x16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 287 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.0 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

BIN
public/icons/icon-24x24.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 381 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

BIN
public/icons/icon-32x32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 437 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

BIN
public/icons/icon-40x40.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 466 B

BIN
public/icons/icon-48x48.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 551 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

BIN
public/icons/icon-60x60.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 638 B

BIN
public/icons/icon-64x64.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 684 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 750 B

BIN
public/icons/icon-76x76.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 783 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 984 B

View File

@@ -9,14 +9,76 @@
manifest.json provides metadata used when your web app is added to the
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" crossorigin="use-credentials">
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
<link rel="apple-touch-icon" href="%PUBLIC_URL%/icons/shlink-128.png">
<link rel="apple-touch-icon" sizes="64x64" href="%PUBLIC_URL%/icons/shlink-64.png">
<link rel="apple-touch-icon" sizes="32x32" href="%PUBLIC_URL%/icons/shlink-32.png">
<link rel="apple-touch-icon" sizes="24x24" href="%PUBLIC_URL%/icons/shlink-24.png">
<link rel="apple-touch-icon" sizes="16x16" href="%PUBLIC_URL%/icons/shlink-16.png">
<!-- FavIcon itself -->
<link rel="icon" type="image/x-icon" href="%PUBLIC_URL%/favicon.ico">
<link rel="icon" type="image/svg+xml" href="%PUBLIC_URL%/favicon.svg" sizes="any">
<link rel="icon" type="image/png" href="%PUBLIC_URL%/favicon.png">
<link rel="icon" type="image/gif" href="%PUBLIC_URL%/favicon.gif">
<!-- Apple Touch -->
<link rel="apple-touch-icon" sizes="16x16" href="%PUBLIC_URL%/icons/icon-16x16.png">
<link rel="apple-touch-icon" sizes="24x24" href="%PUBLIC_URL%/icons/icon-24x24.png">
<link rel="apple-touch-icon" sizes="32x32" href="%PUBLIC_URL%/icons/icon-32x32.png">
<link rel="apple-touch-icon" sizes="40x40" href="%PUBLIC_URL%/icons/icon-40x40.png">
<link rel="apple-touch-icon" sizes="48x48" href="%PUBLIC_URL%/icons/icon-48x48.png">
<link rel="apple-touch-icon" sizes="60x60" href="%PUBLIC_URL%/icons/icon-60x60.png">
<link rel="apple-touch-icon" sizes="64x64" href="%PUBLIC_URL%/icons/icon-64x64.png">
<link rel="apple-touch-icon" sizes="72x72" href="%PUBLIC_URL%/icons/icon-72x72.png">
<link rel="apple-touch-icon" sizes="76x76" href="%PUBLIC_URL%/icons/icon-76x76.png">
<link rel="apple-touch-icon" sizes="96x96" href="%PUBLIC_URL%/icons/icon-96x96.png">
<link rel="apple-touch-icon" sizes="114x114" href="%PUBLIC_URL%/icons/icon-114x114.png">
<link rel="apple-touch-icon" sizes="120x120" href="%PUBLIC_URL%/icons/icon-120x120.png">
<link rel="apple-touch-icon" sizes="128x128" href="%PUBLIC_URL%/icons/icon-128x128.png">
<link rel="apple-touch-icon" sizes="144x144" href="%PUBLIC_URL%/icons/icon-144x144.png">
<link rel="apple-touch-icon" sizes="150x150" href="%PUBLIC_URL%/icons/icon-150x150.png">
<link rel="apple-touch-icon" sizes="152x152" href="%PUBLIC_URL%/icons/icon-152x152.png">
<link rel="apple-touch-icon" sizes="160x160" href="%PUBLIC_URL%/icons/icon-160x160.png">
<link rel="apple-touch-icon" sizes="167x167" href="%PUBLIC_URL%/icons/icon-167x167.png">
<link rel="apple-touch-icon" sizes="180x180" href="%PUBLIC_URL%/icons/icon-180x180.png">
<link rel="apple-touch-icon" sizes="192x192" href="%PUBLIC_URL%/icons/icon-192x192.png">
<link rel="apple-touch-icon" sizes="196x196" href="%PUBLIC_URL%/icons/icon-196x196.png">
<link rel="apple-touch-icon" sizes="228x228" href="%PUBLIC_URL%/icons/icon-228x228.png">
<link rel="apple-touch-icon" sizes="256x256" href="%PUBLIC_URL%/icons/icon-256x256.png">
<link rel="apple-touch-icon" sizes="310x310" href="%PUBLIC_URL%/icons/icon-310x310.png">
<link rel="apple-touch-icon" sizes="384x384" href="%PUBLIC_URL%/icons/icon-384x384.png">
<link rel="apple-touch-icon" sizes="512x512" href="%PUBLIC_URL%/icons/icon-512x512.png">
<link rel="apple-touch-icon" sizes="1024x1024" href="%PUBLIC_URL%/icons/icon-1024x1024.png">
<!-- Normal -->
<link rel="icon" type="image/png" sizes="1024x1024" href="%PUBLIC_URL%/icons/icon-1024x1024.png">
<link rel="icon" type="image/png" sizes="512x512" href="%PUBLIC_URL%/icons/icon-512x512.png">
<link rel="icon" type="image/png" sizes="384x384" href="%PUBLIC_URL%/icons/icon-384x384.png">
<link rel="icon" type="image/png" sizes="310x310" href="%PUBLIC_URL%/icons/icon-310x310.png">
<link rel="icon" type="image/png" sizes="256x256" href="%PUBLIC_URL%/icons/icon-256x256.png">
<link rel="icon" type="image/png" sizes="228x228" href="%PUBLIC_URL%/icons/icon-228x228.png">
<link rel="icon" type="image/png" sizes="196x196" href="%PUBLIC_URL%/icons/icon-196x196.png">
<link rel="icon" type="image/png" sizes="192x192" href="%PUBLIC_URL%/icons/icon-192x192.png">
<link rel="icon" type="image/png" sizes="180x180" href="%PUBLIC_URL%/icons/icon-180x180.png">
<link rel="icon" type="image/png" sizes="167x167" href="%PUBLIC_URL%/icons/icon-167x167.png">
<link rel="icon" type="image/png" sizes="160x160" href="%PUBLIC_URL%/icons/icon-160x160.png">
<link rel="icon" type="image/png" sizes="152x152" href="%PUBLIC_URL%/icons/icon-152x152.png">
<link rel="icon" type="image/png" sizes="150x150" href="%PUBLIC_URL%/icons/icon-150x150.png">
<link rel="icon" type="image/png" sizes="144x144" href="%PUBLIC_URL%/icons/icon-144x144.png">
<link rel="icon" type="image/png" sizes="128x128" href="%PUBLIC_URL%/icons/icon-128x128.png">
<link rel="icon" type="image/png" sizes="120x120" href="%PUBLIC_URL%/icons/icon-120x120.png">
<link rel="icon" type="image/png" sizes="114x114" href="%PUBLIC_URL%/icons/icon-114x114.png">
<link rel="icon" type="image/png" sizes="96x96" href="%PUBLIC_URL%/icons/icon-96x96.png">
<link rel="icon" type="image/png" sizes="76x76" href="%PUBLIC_URL%/icons/icon-76x76.png">
<link rel="icon" type="image/png" sizes="72x72" href="%PUBLIC_URL%/icons/icon-72x72.png">
<link rel="icon" type="image/png" sizes="64x64" href="%PUBLIC_URL%/icons/icon-64x64.png">
<link rel="icon" type="image/png" sizes="60x60" href="%PUBLIC_URL%/icons/icon-60x60.png">
<link rel="icon" type="image/png" sizes="48x48" href="%PUBLIC_URL%/icons/icon-48x48.png">
<link rel="icon" type="image/png" sizes="40x40" href="%PUBLIC_URL%/icons/icon-40x40.png">
<link rel="icon" type="image/png" sizes="32x32" href="%PUBLIC_URL%/icons/icon-32x32.png">
<link rel="icon" type="image/png" sizes="24x24" href="%PUBLIC_URL%/icons/icon-24x24.png">
<link rel="icon" type="image/png" sizes="16x16" href="%PUBLIC_URL%/icons/icon-16x16.png">
<!-- MS -->
<meta name="msapplication-TileImage" content="%PUBLIC_URL%/icons/icon-144x144.png">
<meta name="msapplication-square70x70logo" content="%PUBLIC_URL%/icons/icon-70x70.png">
<meta name="msapplication-square144x144logo" content="%PUBLIC_URL%/icons/icon-144x144.png">
<meta name="msapplication-square150x150logo" content="%PUBLIC_URL%/icons/icon-150x150.png">
<meta name="msapplication-square310x310logo" content="%PUBLIC_URL%/icons/icon-310x310.png">
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.

View File

@@ -6,16 +6,66 @@
"theme_color": "#4696e5",
"background_color": "#4696e5",
"icons": [
{
"src": "./icons/icon-16x16.png",
"type": "image/png",
"sizes": "16x16"
},
{
"src": "./icons/icon-24x24.png",
"type": "image/png",
"sizes": "24x24"
},
{
"src": "./icons/icon-32x32.png",
"type": "image/png",
"sizes": "32x32"
},
{
"src": "./icons/icon-40x40.png",
"type": "image/png",
"sizes": "40x40"
},
{
"src": "./icons/icon-48x48.png",
"type": "image/png",
"sizes": "48x48"
},
{
"src": "./icons/icon-60x60.png",
"type": "image/png",
"sizes": "60x60"
},
{
"src": "./icons/icon-64x64.png",
"type": "image/png",
"sizes": "64x64"
},
{
"src": "./icons/icon-72x72.png",
"type": "image/png",
"sizes": "72x72"
},
{
"src": "./icons/icon-76x76.png",
"type": "image/png",
"sizes": "76x76"
},
{
"src": "./icons/icon-96x96.png",
"type": "image/png",
"sizes": "96x96"
},
{
"src": "./icons/icon-114x114.png",
"type": "image/png",
"sizes": "114x114"
},
{
"src": "./icons/icon-120x120.png",
"type": "image/png",
"sizes": "120x120"
},
{
"src": "./icons/icon-128x128.png",
"type": "image/png",
@@ -26,20 +76,70 @@
"type": "image/png",
"sizes": "144x144"
},
{
"src": "./icons/icon-150x150.png",
"type": "image/png",
"sizes": "150x150"
},
{
"src": "./icons/icon-152x152.png",
"type": "image/png",
"sizes": "152x152"
},
{
"src": "./icons/icon-160x160.png",
"type": "image/png",
"sizes": "160x160"
},
{
"src": "./icons/icon-167x167.png",
"type": "image/png",
"sizes": "167x167"
},
{
"src": "./icons/icon-180x180.png",
"type": "image/png",
"sizes": "180x180"
},
{
"src": "./icons/icon-192x192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "./icons/icon-196x196.png",
"type": "image/png",
"sizes": "196x196"
},
{
"src": "./icons/icon-228x228.png",
"type": "image/png",
"sizes": "228x228"
},
{
"src": "./icons/icon-256x256.png",
"type": "image/png",
"sizes": "256x256"
},
{
"src": "./icons/icon-310x310.png",
"type": "image/png",
"sizes": "310x310"
},
{
"src": "./icons/icon-384x384.png",
"type": "image/png",
"sizes": "384x384"
},
{
"src": "./icons/icon-512x512.png",
"type": "image/png",
"sizes": "512x512"
},
{
"src": "./icons/icon-1024x1024.png",
"type": "image/png",
"sizes": "1024x1024"
}
]
}

View File

@@ -1,4 +1,4 @@
/* eslint-disable no-console */
/* eslint-disable no-console, @typescript-eslint/prefer-nullish-coalescing, @typescript-eslint/promise-function-async, @typescript-eslint/prefer-optional-chain */
// Do this as the first thing so that any code reading it knows the right env.
process.env.BABEL_ENV = 'production';
@@ -14,7 +14,6 @@ process.on('unhandledRejection', (err) => {
// Ensure environment variables are read.
require('../config/env');
const path = require('path');
const chalk = require('chalk');
const fs = require('fs-extra');
const webpack = require('webpack');
@@ -22,7 +21,6 @@ const bfj = require('bfj');
const AdmZip = require('adm-zip');
const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles');
const formatWebpackMessages = require('react-dev-utils/formatWebpackMessages');
const printHostingInstructions = require('react-dev-utils/printHostingInstructions');
const FileSizeReporter = require('react-dev-utils/FileSizeReporter');
const printBuildError = require('react-dev-utils/printBuildError');
const { checkBrowsers } = require('react-dev-utils/browsersHelper');
@@ -30,7 +28,6 @@ const paths = require('../config/paths');
const configFactory = require('../config/webpack.config');
const { measureFileSizesBeforeBuild, printFileSizesAfterBuild } = FileSizeReporter;
const useYarn = fs.existsSync(paths.yarnLockFile);
// These sizes are pretty large. We'll warn for bundles exceeding them.
const WARN_AFTER_BUNDLE_GZIP_SIZE = 512 * 1024; // eslint-disable-line
@@ -46,7 +43,9 @@ if (!checkRequiredFiles([ paths.appHtml, paths.appIndexJs ])) {
// Process CLI arguments
const argvSliceStart = 2;
const argv = process.argv.slice(argvSliceStart);
const writeStatsJson = argv.indexOf('--stats') !== -1;
const writeStatsJson = argv.includes('--stats');
const withoutDist = argv.includes('--no-dist');
const { version, hasVersion } = getVersionFromArgs(argv);
// Generate configuration
const config = configFactory('production');
@@ -76,15 +75,16 @@ checkBrowsers(paths.appPath, isInteractive)
console.log(
`\nSearch for the ${
chalk.underline(chalk.yellow('keywords'))
} to learn more about each warning.`
} to learn more about each warning.`,
);
console.log(
`To ignore, add ${
chalk.cyan('// eslint-disable-next-line')
} to the line before.\n`
} to the line before.\n`,
);
} else {
console.log(chalk.green('Compiled successfully.\n'));
hasVersion && replaceVersionPlaceholder(version);
}
console.log('File sizes after gzip:\n');
@@ -93,31 +93,17 @@ checkBrowsers(paths.appPath, isInteractive)
previousFileSizes,
paths.appBuild,
WARN_AFTER_BUNDLE_GZIP_SIZE,
WARN_AFTER_CHUNK_GZIP_SIZE
WARN_AFTER_CHUNK_GZIP_SIZE,
);
console.log();
const appPackage = require(paths.appPackageJson);
const { publicUrl } = paths;
const { output: { publicPath } } = config;
const buildFolder = path.relative(process.cwd(), paths.appBuild);
printHostingInstructions(
appPackage,
publicUrl,
publicPath,
buildFolder,
useYarn
);
},
(err) => {
console.log(chalk.red('Failed to compile.\n'));
printBuildError(err);
process.exit(1);
}
},
)
.then(zipDist)
.then(() => hasVersion && !withoutDist && zipDist(version))
.catch((err) => {
if (err && err.message) {
console.log(err.message);
@@ -147,7 +133,7 @@ function build(previousFileSizes) {
});
} else {
messages = formatWebpackMessages(
stats.toJson({ all: false, warnings: true, errors: true })
stats.toJson({ all: false, warnings: true, errors: true }),
);
}
if (messages.errors.length) {
@@ -168,8 +154,8 @@ function build(previousFileSizes) {
console.log(
chalk.yellow(
'\nTreating warnings as errors because process.env.CI = true.\n' +
'Most CI servers set it automatically.\n'
)
'Most CI servers set it automatically.\n',
),
);
return reject(new Error(messages.warnings.join('\n\n')));
@@ -200,15 +186,7 @@ function copyPublicFolder() {
});
}
function zipDist() {
const minArgsToContainVersion = 3;
// If no version was provided, do nothing
if (process.argv.length < minArgsToContainVersion) {
return;
}
const [ , , version ] = process.argv;
function zipDist(version) {
const versionFileName = `./dist/shlink-web-client_${version}_dist.zip`;
console.log(chalk.cyan(`Generating dist file for version ${chalk.bold(version)}...`));
@@ -226,4 +204,24 @@ function zipDist() {
console.log(chalk.red('An error occurred while generating dist file'));
console.log(e);
}
console.log();
}
function getVersionFromArgs(argv) {
const [ version ] = argv;
return { version, hasVersion: !!version };
}
function replaceVersionPlaceholder(version) {
const staticJsFilesPath = './build/static/js';
const versionPlaceholder = '%_VERSION_%';
const isMainFile = (file) => file.startsWith('main.') && file.endsWith('.js');
const [ mainJsFile ] = fs.readdirSync(staticJsFilesPath).filter(isMainFile);
const filePath = `${staticJsFilesPath}/${mainJsFile}`;
const fileContent = fs.readFileSync(filePath, 'utf-8');
const replaced = fileContent.replace(versionPlaceholder, version);
fs.writeFileSync(filePath, replaced, 'utf-8');
}

25
scripts/docker/build Executable file
View File

@@ -0,0 +1,25 @@
#!/bin/bash
set -ex
PLATFORMS="linux/arm/v7,linux/arm64/v8,linux/amd64"
DOCKER_IMAGE="shlinkio/shlink-web-client"
if [[ "$GITHUB_REF" == *"main"* ]]; then
docker buildx build --push \
--platform ${PLATFORMS} \
-t ${DOCKER_IMAGE}:latest .
# If ref is not main, then this is a tag. Build that docker tag and also "stable"
else
VERSION=${GITHUB_REF#refs/tags/v}
TAGS="-t ${DOCKER_IMAGE}:${VERSION}"
# Push stable tag only if this is not an alpha or beta release
[[ $GITHUB_REF != *"alpha"* && $GITHUB_REF != *"beta"* ]] && TAGS="${TAGS} -t ${DOCKER_IMAGE}:stable"
docker buildx build --push \
--build-arg VERSION=${VERSION} \
--platform ${PLATFORMS} \
${TAGS} .
fi

View File

@@ -1,4 +1,4 @@
/* eslint-disable no-console */
/* eslint-disable no-console, @typescript-eslint/prefer-nullish-coalescing, @typescript-eslint/promise-function-async, @typescript-eslint/prefer-optional-chain */
// Do this as the first thing so that any code reading it knows the right env.
process.env.BABEL_ENV = 'development';
@@ -49,15 +49,15 @@ if (process.env.HOST) {
console.log(
chalk.cyan(
`Attempting to bind to HOST environment variable: ${chalk.yellow(
chalk.bold(process.env.HOST)
)}`
)
chalk.bold(process.env.HOST),
)}`,
),
);
console.log(
'If this was unintentional, check that you haven\'t mistakenly set it in your shell.'
'If this was unintentional, check that you haven\'t mistakenly set it in your shell.',
);
console.log(
`Learn more here: ${chalk.yellow('http://bit.ly/CRA-advanced-config')}`
`Learn more here: ${chalk.yellow('http://bit.ly/CRA-advanced-config')}`,
);
console.log();
}
@@ -91,7 +91,7 @@ checkBrowsers(paths.appPath, isInteractive)
// Serve webpack assets generated by the compiler over a web server.
const serverConfig = createDevServerConfig(
proxyConfig,
urls.lanUrlForConfig
urls.lanUrlForConfig,
);
const devServer = new WebpackDevServer(compiler, serverConfig);

17
shlink-web-client.d.ts vendored Normal file
View File

@@ -0,0 +1,17 @@
declare module 'event-source-polyfill' {
declare class EventSourcePolyfill {
public onmessage?: ({ data }: { data: string }) => void;
public onerror?: ({ status }: { status: number }) => void;
public close: () => void;
public constructor(hubUrl: URL, options?: any);
}
}
declare module 'csvjson' {
export declare class CsvJson {
public toObject<T>(content: string): T[];
public toCSV<T>(data: T[], options: { headers: 'full' | 'none' | 'relative' | 'key' }): string;
}
}
declare module '*.png'

BIN
shlink-web-client.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

View File

@@ -1,21 +0,0 @@
import React from 'react';
import { Route, Switch } from 'react-router-dom';
import './App.scss';
import NotFound from './common/NotFound';
const App = (MainHeader, Home, MenuLayout, CreateServer) => () => (
<div className="container-fluid app-container">
<MainHeader />
<div className="app">
<Switch>
<Route exact path="/server/create" component={CreateServer} />
<Route exact path="/" component={Home} />
<Route path="/server/:serverId" component={MenuLayout} />
<Route component={NotFound} />
</Switch>
</div>
</div>
);
export default App;

View File

@@ -8,3 +8,19 @@
padding-top: $headerHeight;
height: 100%;
}
.shlink-wrapper {
min-height: 100%;
padding-bottom: $footer-height + $footer-margin;
margin-bottom: -($footer-height + $footer-margin);
}
.shlink-footer {
height: $footer-height;
margin-top: $footer-margin;
padding: 0;
@media (min-width: $mdMin) {
padding: 0 15px;
}
}

57
src/App.tsx Normal file
View File

@@ -0,0 +1,57 @@
import { useEffect, FC } from 'react';
import { Route, Switch } from 'react-router-dom';
import NotFound from './common/NotFound';
import { ServersMap } from './servers/data';
import { Settings } from './settings/reducers/settings';
import { changeThemeInMarkup } from './utils/theme';
import './App.scss';
interface AppProps {
fetchServers: Function;
servers: ServersMap;
settings: Settings;
}
const App = (
MainHeader: FC,
Home: FC,
MenuLayout: FC,
CreateServer: FC,
EditServer: FC,
Settings: FC,
ShlinkVersionsContainer: FC,
) => ({ fetchServers, servers, settings }: AppProps) => {
useEffect(() => {
// On first load, try to fetch the remote servers if the list is empty
if (Object.keys(servers).length === 0) {
fetchServers();
}
changeThemeInMarkup(settings.ui?.theme ?? 'light');
}, []);
return (
<div className="container-fluid app-container">
<MainHeader />
<div className="app">
<div className="shlink-wrapper">
<Switch>
<Route exact path="/" component={Home} />
<Route exact path="/settings" component={Settings} />
<Route exact path="/server/create" component={CreateServer} />
<Route exact path="/server/:serverId/edit" component={EditServer} />
<Route path="/server/:serverId" component={MenuLayout} />
<Route component={NotFound} />
</Switch>
</div>
<div className="shlink-footer">
<ShlinkVersionsContainer />
</div>
</div>
</div>
);
};
export default App;

View File

@@ -0,0 +1,16 @@
import { ProblemDetailsError } from './types';
import { isInvalidArgumentError } from './utils';
export interface ShlinkApiErrorProps {
errorData?: ProblemDetailsError;
fallbackMessage?: string;
}
export const ShlinkApiError = ({ errorData, fallbackMessage }: ShlinkApiErrorProps) => (
<>
{errorData?.detail ?? fallbackMessage}
{isInvalidArgumentError(errorData) &&
<p className="mb-0">Invalid elements: [{errorData.invalidElements.join(', ')}]</p>
}
</>
);

View File

@@ -0,0 +1,142 @@
import qs from 'qs';
import { isEmpty, isNil, reject } from 'ramda';
import { AxiosInstance, AxiosResponse, Method } from 'axios';
import { ShortUrlsListParams } from '../../short-urls/reducers/shortUrlsListParams';
import { ShortUrl, ShortUrlData } from '../../short-urls/data';
import { OptionalString } from '../../utils/utils';
import {
ShlinkHealth,
ShlinkMercureInfo,
ShlinkShortUrlsResponse,
ShlinkTags,
ShlinkTagsResponse,
ShlinkVisits,
ShlinkVisitsParams,
ShlinkShortUrlData,
ShlinkDomain,
ShlinkDomainsResponse,
ShlinkVisitsOverview,
} from '../types';
const buildShlinkBaseUrl = (url: string, apiVersion: number) => url ? `${url}/rest/v${apiVersion}` : '';
const rejectNilProps = reject(isNil);
export default class ShlinkApiClient {
private apiVersion: number;
public constructor(
private readonly axios: AxiosInstance,
private readonly baseUrl: string,
private readonly apiKey: string,
) {
this.apiVersion = 2;
}
public readonly listShortUrls = async (params: ShortUrlsListParams = {}): Promise<ShlinkShortUrlsResponse> =>
this.performRequest<{ shortUrls: ShlinkShortUrlsResponse }>('/short-urls', 'GET', params)
.then(({ data }) => data.shortUrls);
public readonly createShortUrl = async (options: ShortUrlData): Promise<ShortUrl> => {
const filteredOptions = reject((value) => isEmpty(value) || isNil(value), options as any);
return this.performRequest<ShortUrl>('/short-urls', 'POST', {}, filteredOptions)
.then((resp) => resp.data);
};
public readonly getShortUrlVisits = async (shortCode: string, query?: ShlinkVisitsParams): Promise<ShlinkVisits> =>
this.performRequest<{ visits: ShlinkVisits }>(`/short-urls/${shortCode}/visits`, 'GET', query)
.then(({ data }) => data.visits);
public readonly getTagVisits = async (tag: string, query?: Omit<ShlinkVisitsParams, 'domain'>): Promise<ShlinkVisits> =>
this.performRequest<{ visits: ShlinkVisits }>(`/tags/${tag}/visits`, 'GET', query)
.then(({ data }) => data.visits);
public readonly getOrphanVisits = async (query?: Omit<ShlinkVisitsParams, 'domain'>): Promise<ShlinkVisits> =>
this.performRequest<{ visits: ShlinkVisits }>('/visits/orphan', 'GET', query)
.then(({ data }) => data.visits);
public readonly getVisitsOverview = async (): Promise<ShlinkVisitsOverview> =>
this.performRequest<{ visits: ShlinkVisitsOverview }>('/visits', 'GET')
.then(({ data }) => data.visits);
public readonly getShortUrl = async (shortCode: string, domain?: OptionalString): Promise<ShortUrl> =>
this.performRequest<ShortUrl>(`/short-urls/${shortCode}`, 'GET', { domain })
.then(({ data }) => data);
public readonly deleteShortUrl = async (shortCode: string, domain?: OptionalString): Promise<void> =>
this.performRequest(`/short-urls/${shortCode}`, 'DELETE', { domain })
.then(() => {});
/* @deprecated. If using Shlink 2.6.0 or greater, use updateShortUrl instead */
public readonly updateShortUrlTags = async (
shortCode: string,
domain: OptionalString,
tags: string[],
): Promise<string[]> =>
this.performRequest<{ tags: string[] }>(`/short-urls/${shortCode}/tags`, 'PUT', { domain }, { tags })
.then(({ data }) => data.tags);
public readonly updateShortUrl = async (
shortCode: string,
domain: OptionalString,
data: ShlinkShortUrlData,
): Promise<ShortUrl> =>
this.performRequest<ShortUrl>(`/short-urls/${shortCode}`, 'PATCH', { domain }, data)
.then(({ data }) => data);
public readonly listTags = async (): Promise<ShlinkTags> =>
this.performRequest<{ tags: ShlinkTagsResponse }>('/tags', 'GET', { withStats: 'true' })
.then((resp) => resp.data.tags)
.then(({ data, stats }) => ({ tags: data, stats }));
public readonly deleteTags = async (tags: string[]): Promise<{ tags: string[] }> =>
this.performRequest('/tags', 'DELETE', { tags })
.then(() => ({ tags }));
public readonly editTag = async (oldName: string, newName: string): Promise<{ oldName: string; newName: string }> =>
this.performRequest('/tags', 'PUT', {}, { oldName, newName })
.then(() => ({ oldName, newName }));
public readonly health = async (): Promise<ShlinkHealth> =>
this.performRequest<ShlinkHealth>('/health', 'GET')
.then((resp) => resp.data);
public readonly mercureInfo = async (): Promise<ShlinkMercureInfo> =>
this.performRequest<ShlinkMercureInfo>('/mercure-info', 'GET')
.then((resp) => resp.data);
public readonly listDomains = async (): Promise<ShlinkDomain[]> =>
this.performRequest<{ domains: ShlinkDomainsResponse }>('/domains', 'GET').then(({ data }) => data.domains.data);
private readonly performRequest = async <T>(url: string, method: Method = 'GET', query = {}, body = {}): Promise<AxiosResponse<T>> => {
try {
return await this.axios({
method,
url: `${buildShlinkBaseUrl(this.baseUrl, this.apiVersion)}${url}`,
headers: { 'X-Api-Key': this.apiKey },
params: rejectNilProps(query),
data: body,
paramsSerializer: (params) => qs.stringify(params, { arrayFormat: 'brackets' }),
});
} catch (e) {
const { response } = e;
// Due to a bug on all previous Shlink versions, requests to non-matching URLs will always result on a CORS error
// when performed from the browser (due to the preflight request not returning a 2xx status.
// See https://github.com/shlinkio/shlink/issues/614), which will make the "response" prop not to be set here.
// The bug will be fixed on upcoming Shlink patches, but for other versions, we can consider this situation as
// if a request has been performed to a not supported API version.
const apiVersionIsNotSupported = !response;
// When the request is not invalid or we have already tried both API versions, throw the error and let the
// caller handle it
if (!apiVersionIsNotSupported || this.apiVersion === 2) {
throw e;
}
this.apiVersion = this.apiVersion - 1;
return await this.performRequest(url, method, query, body);
}
};
}

View File

@@ -0,0 +1,36 @@
import { AxiosInstance } from 'axios';
import { prop } from 'ramda';
import { hasServerData, SelectedServer, ServerWithId } from '../../servers/data';
import { GetState } from '../../container/types';
import ShlinkApiClient from './ShlinkApiClient';
const apiClients: Record<string, ShlinkApiClient> = {};
const isGetState = (getStateOrSelectedServer: GetState | ServerWithId): getStateOrSelectedServer is GetState =>
typeof getStateOrSelectedServer === 'function';
const getSelectedServerFromState = (getState: GetState): SelectedServer => prop('selectedServer', getState());
export type ShlinkApiClientBuilder = (getStateOrSelectedServer: GetState | ServerWithId) => ShlinkApiClient;
const buildShlinkApiClient = (axios: AxiosInstance): ShlinkApiClientBuilder => (
getStateOrSelectedServer: GetState | ServerWithId,
) => {
const server = isGetState(getStateOrSelectedServer)
? getSelectedServerFromState(getStateOrSelectedServer)
: getStateOrSelectedServer;
if (!hasServerData(server)) {
throw new Error('There\'s no selected server or it is not found');
}
const { url, apiKey } = server;
const clientKey = `${url}_${apiKey}`;
if (!apiClients[clientKey]) {
apiClients[clientKey] = new ShlinkApiClient(axios, url, apiKey);
}
return apiClients[clientKey];
};
export default buildShlinkApiClient;

View File

@@ -0,0 +1,8 @@
import Bottle from 'bottlejs';
import buildShlinkApiClient from './ShlinkApiClientBuilder';
const provideServices = (bottle: Bottle) => {
bottle.serviceFactory('buildShlinkApiClient', buildShlinkApiClient, 'axios');
};
export default provideServices;

93
src/api/types/index.ts Normal file
View File

@@ -0,0 +1,93 @@
import { Visit } from '../../visits/types';
import { OptionalString } from '../../utils/utils';
import { ShortUrl, ShortUrlMeta } from '../../short-urls/data';
export interface ShlinkShortUrlsResponse {
data: ShortUrl[];
pagination: ShlinkPaginator;
}
export interface ShlinkMercureInfo {
token: string;
mercureHubUrl: string;
}
export interface ShlinkHealth {
status: 'pass' | 'fail';
version: string;
}
interface ShlinkTagsStats {
tag: string;
shortUrlsCount: number;
visitsCount: number;
}
export interface ShlinkTags {
tags: string[];
stats?: ShlinkTagsStats[]; // Is only optional in Shlink older than v2.2
}
export interface ShlinkTagsResponse {
data: string[];
stats?: ShlinkTagsStats[]; // Is only optional in Shlink older than v2.2
}
export interface ShlinkPaginator {
currentPage: number;
pagesCount: number;
totalItems: number;
}
export interface ShlinkVisits {
data: Visit[];
pagination: ShlinkPaginator;
}
export interface ShlinkVisitsOverview {
visitsCount: number;
orphanVisitsCount?: number; // Optional only for versions older than 2.6.0
}
export interface ShlinkVisitsParams {
domain?: OptionalString;
page?: number;
itemsPerPage?: number;
startDate?: string;
endDate?: string;
}
export interface ShlinkShortUrlData extends ShortUrlMeta {
longUrl?: string;
title?: string;
validateUrl?: boolean;
tags?: string[];
}
export interface ShlinkDomain {
domain: string;
isDefault: boolean;
}
export interface ShlinkDomainsResponse {
data: ShlinkDomain[];
}
export interface ProblemDetailsError {
type: string;
detail: string;
title: string;
status: number;
[extraProps: string]: any;
}
export interface InvalidArgumentError extends ProblemDetailsError {
type: 'INVALID_ARGUMENT';
invalidElements: string[];
}
export interface InvalidShortUrlDeletion extends ProblemDetailsError {
type: 'INVALID_SHORTCODE_DELETION';
threshold: number;
}

10
src/api/utils/index.ts Normal file
View File

@@ -0,0 +1,10 @@
import { AxiosError } from 'axios';
import { InvalidArgumentError, InvalidShortUrlDeletion, ProblemDetailsError } from '../types';
export const parseApiError = (e: AxiosError<ProblemDetailsError>) => e.response?.data;
export const isInvalidArgumentError = (error?: ProblemDetailsError): error is InvalidArgumentError =>
error?.type === 'INVALID_ARGUMENT';
export const isInvalidDeletionError = (error?: ProblemDetailsError): error is InvalidShortUrlDeletion =>
error?.type === 'INVALID_SHORTCODE_DELETION';

View File

@@ -1,73 +0,0 @@
import { faList as listIcon, faLink as createIcon, faTags as tagsIcon } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import React from 'react';
import { NavLink } from 'react-router-dom';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import { serverType } from '../servers/prop-types';
import './AsideMenu.scss';
const defaultProps = {
className: '',
showOnMobile: false,
};
const propTypes = {
selectedServer: serverType,
className: PropTypes.string,
showOnMobile: PropTypes.bool,
};
const AsideMenu = (DeleteServerButton) => {
const AsideMenu = ({ selectedServer, className, showOnMobile }) => {
const serverId = selectedServer ? selectedServer.id : '';
const asideClass = classnames('aside-menu', className, {
'aside-menu--hidden': !showOnMobile,
});
const shortUrlsIsActive = (match, location) => location.pathname.match('/list-short-urls');
return (
<aside className={asideClass}>
<nav className="nav flex-column aside-menu__nav">
<NavLink
className="aside-menu__item"
activeClassName="aside-menu__item--selected"
to={`/server/${serverId}/list-short-urls/1`}
isActive={shortUrlsIsActive}
>
<FontAwesomeIcon icon={listIcon} />
<span className="aside-menu__item-text">List short URLs</span>
</NavLink>
<NavLink
className="aside-menu__item"
activeClassName="aside-menu__item--selected"
to={`/server/${serverId}/create-short-url`}
>
<FontAwesomeIcon icon={createIcon} flip="horizontal" />
<span className="aside-menu__item-text">Create short URL</span>
</NavLink>
<NavLink
className="aside-menu__item"
activeClassName="aside-menu__item--selected"
to={`/server/${serverId}/manage-tags`}
>
<FontAwesomeIcon icon={tagsIcon} />
<span className="aside-menu__item-text">Manage tags</span>
</NavLink>
<DeleteServerButton
className="aside-menu__item aside-menu__item--danger"
server={selectedServer}
/>
</nav>
</aside>
);
};
AsideMenu.defaultProps = defaultProps;
AsideMenu.propTypes = propTypes;
return AsideMenu;
};
export default AsideMenu;

View File

@@ -1,10 +1,10 @@
@import '../utils/base';
@import '../utils/mixins/vertical-align';
$asideMenuMobileWidth: 280px;
.aside-menu {
background-color: #f7f7f7;
width: $asideMenuWidth;
background-color: var(--primary-color);
box-shadow: rgba(0, 0, 0, .05) 0 8px 15px;
position: fixed !important;
padding-top: 13px;
padding-bottom: 10px;
@@ -18,11 +18,9 @@ $asideMenuMobileWidth: 280px;
@media (min-width: $mdMin) {
padding: 30px 15px 15px;
border-right: 1px solid #eee;
}
@media (max-width: $smMax) {
width: $asideMenuMobileWidth !important;
transition: left 300ms;
top: $headerHeight - 3px;
box-shadow: -10px 0 50px 11px rgba(0, 0, 0, .55);
@@ -31,7 +29,7 @@ $asideMenuMobileWidth: 280px;
.aside-menu--hidden {
@media (max-width: $smMax) {
left: -($asideMenuMobileWidth + 35px);
left: -($asideMenuWidth + 35px);
}
}
@@ -44,34 +42,37 @@ $asideMenuMobileWidth: 280px;
margin: 0 -15px;
text-decoration: none !important;
cursor: pointer;
@media (max-width: $smMax) {
margin: 0;
}
}
.aside-menu__item:hover {
background-color: $lightHoverColor;
}
.aside-menu__item--selected {
color: #fff;
background-color: $mainColor;
background-color: var(--secondary-color);
}
.aside-menu__item--selected,
.aside-menu__item--selected:hover {
color: #fff;
background-color: $mainColor;
color: #ffffff;
background-color: var(--brand-color);
}
.aside-menu__item--divider {
border-bottom: 1px solid #eee;
border-bottom: 1px solid #eeeeee;
margin: 20px 0;
}
.aside-menu__item--danger {
color: $dangerColor;
}
.aside-menu__item--push {
margin-top: auto;
}
.aside-menu__item--danger:hover {
color: #fff;
color: #ffffff;
background-color: $dangerColor;
}

81
src/common/AsideMenu.tsx Normal file
View File

@@ -0,0 +1,81 @@
import {
faList as listIcon,
faLink as createIcon,
faTags as tagsIcon,
faPen as editIcon,
faHome as overviewIcon,
} from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { FC } from 'react';
import { NavLink, NavLinkProps } from 'react-router-dom';
import classNames from 'classnames';
import { Location } from 'history';
import { DeleteServerButtonProps } from '../servers/DeleteServerButton';
import { ServerWithId } from '../servers/data';
import './AsideMenu.scss';
export interface AsideMenuProps {
selectedServer: ServerWithId;
className?: string;
showOnMobile?: boolean;
}
interface AsideMenuItemProps extends NavLinkProps {
to: string;
}
const AsideMenuItem: FC<AsideMenuItemProps> = ({ children, to, className, ...rest }) => (
<NavLink
className={classNames('aside-menu__item', className)}
activeClassName="aside-menu__item--selected"
to={to}
{...rest}
>
{children}
</NavLink>
);
const AsideMenu = (DeleteServerButton: FC<DeleteServerButtonProps>) => (
{ selectedServer, showOnMobile = false }: AsideMenuProps,
) => {
const serverId = selectedServer ? selectedServer.id : '';
const asideClass = classNames('aside-menu', {
'aside-menu--hidden': !showOnMobile,
});
const shortUrlsIsActive = (_: null, location: Location) => location.pathname.match('/list-short-urls') !== null;
const buildPath = (suffix: string) => `/server/${serverId}${suffix}`;
return (
<aside className={asideClass}>
<nav className="nav flex-column aside-menu__nav">
<AsideMenuItem to={buildPath('/overview')}>
<FontAwesomeIcon icon={overviewIcon} />
<span className="aside-menu__item-text">Overview</span>
</AsideMenuItem>
<AsideMenuItem to={buildPath('/list-short-urls/1')} isActive={shortUrlsIsActive}>
<FontAwesomeIcon icon={listIcon} />
<span className="aside-menu__item-text">List short URLs</span>
</AsideMenuItem>
<AsideMenuItem to={buildPath('/create-short-url')}>
<FontAwesomeIcon icon={createIcon} flip="horizontal" />
<span className="aside-menu__item-text">Create short URL</span>
</AsideMenuItem>
<AsideMenuItem to={buildPath('/manage-tags')}>
<FontAwesomeIcon icon={tagsIcon} />
<span className="aside-menu__item-text">Manage tags</span>
</AsideMenuItem>
<AsideMenuItem to={buildPath('/edit')} className="aside-menu__item--push">
<FontAwesomeIcon icon={editIcon} />
<span className="aside-menu__item-text">Edit this server</span>
</AsideMenuItem>
<DeleteServerButton
className="aside-menu__item aside-menu__item--danger"
textClassName="aside-menu__item-text"
server={selectedServer}
/>
</nav>
</aside>
);
};
export default AsideMenu;

View File

@@ -1,42 +0,0 @@
import React from 'react';
import * as PropTypes from 'prop-types';
import './ErrorHandler.scss';
import { Button } from 'reactstrap';
const ErrorHandler = ({ location }, { error }) => class ErrorHandler extends React.Component {
static propTypes = {
children: PropTypes.node.isRequired,
};
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError() {
return { hasError: true };
}
componentDidCatch(e) {
if (process.env.NODE_ENV !== 'development') {
error(e);
}
}
render() {
if (this.state.hasError) {
return (
<div className="error-handler">
<h1>Oops! This is awkward :S</h1>
<p>It seems that something went wrong. Try refreshing the page or just click this button.</p>
<br />
<Button outline color="primary" onClick={() => location.reload()}>Take me back</Button>
</div>
);
}
return this.props.children;
}
};
export default ErrorHandler;

View File

@@ -1,9 +0,0 @@
@import '../utils/mixins/vertical-align.scss';
.error-handler {
@include vertical-align();
padding: 20px;
text-align: center;
width: 100%;
}

View File

@@ -0,0 +1,46 @@
import { Component, ReactNode } from 'react';
import { Button } from 'reactstrap';
import { SimpleCard } from '../utils/SimpleCard';
interface ErrorHandlerState {
hasError: boolean;
}
const ErrorHandler = (
{ location }: Window,
{ error }: Console,
) => class ErrorHandler extends Component<any, ErrorHandlerState> {
public constructor(props: object) {
super(props);
this.state = { hasError: false };
}
public static getDerivedStateFromError(): ErrorHandlerState {
return { hasError: true };
}
public componentDidCatch(e: Error): void {
if (process.env.NODE_ENV !== 'development') {
error(e);
}
}
public render(): ReactNode {
if (this.state.hasError) {
return (
<div className="home">
<SimpleCard className="p-4">
<h1>Oops! This is awkward :S</h1>
<p>It seems that something went wrong. Try refreshing the page or just click this button.</p>
<br />
<Button outline color="primary" onClick={() => location.reload()}>Take me back</Button>
</SimpleCard>
</div>
);
}
return this.props.children;
}
};
export default ErrorHandler;

View File

@@ -1,52 +0,0 @@
import { faChevronRight as chevronIcon } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { isEmpty, values } from 'ramda';
import React from 'react';
import { Link } from 'react-router-dom';
import { ListGroup, ListGroupItem } from 'reactstrap';
import PropTypes from 'prop-types';
import './Home.scss';
export default class Home extends React.Component {
static propTypes = {
resetSelectedServer: PropTypes.func,
servers: PropTypes.object,
};
componentDidMount() {
this.props.resetSelectedServer();
}
render() {
const { servers: { list, loading } } = this.props;
const servers = values(list);
const hasServers = !isEmpty(servers);
return (
<div className="home">
<h1 className="home__title">Welcome to Shlink</h1>
<h5 className="home__intro">
{!loading && hasServers && <span>Please, select a server.</span>}
{!loading && !hasServers && <span>Please, <Link to="/server/create">add a server</Link>.</span>}
{loading && <span>Trying to load servers...</span>}
</h5>
{!loading && hasServers && (
<ListGroup className="home__servers-list">
{servers.map(({ name, id }) => (
<ListGroupItem
key={id}
tag={Link}
to={`/server/${id}/list-short-urls/1`}
className="home__servers-item"
>
{name}
<FontAwesomeIcon icon={chevronIcon} className="home__servers-item-icon" />
</ListGroupItem>
))}
</ListGroup>
)}
</div>
);
}
}

View File

@@ -2,36 +2,40 @@
@import '../utils/mixins/vertical-align';
.home {
text-align: center;
height: calc(100vh - #{$headerHeight});
display: flex;
align-items: center;
justify-content: center;
flex-flow: column;
position: relative;
padding-top: 15px;
@media (min-width: $mdMin) {
padding-top: 0;
height: calc(100vh - #{$headerHeight} - #{($footer-height + $footer-margin)});
}
}
.home__logo {
@include vertical-align();
}
.home__main-card {
margin: 0 auto;
max-width: 720px;
@media (min-width: $mdMin) {
@include vertical-align();
}
}
.home__title {
text-align: center;
font-size: 1.75rem;
margin: 0;
@media (min-width: $mdMin) {
font-size: 2.2rem;
}
}
.home__servers-list {
margin-top: 1rem;
width: 100%;
max-width: 400px;
}
.home__servers-item.home__servers-item {
text-align: left;
position: relative;
padding: .75rem 2.5rem .75rem 1rem;
}
.home__servers-item-icon {
@include vertical-align();
right: 1rem;
.home__servers-container {
@media (min-width: $mdMin) {
border-left: 1px solid var(--border-color);
}
}

50
src/common/Home.tsx Normal file
View File

@@ -0,0 +1,50 @@
import { isEmpty, values } from 'ramda';
import { Link } from 'react-router-dom';
import { Card, Row } from 'reactstrap';
import { ExternalLink } from 'react-external-link';
import ServersListGroup from '../servers/ServersListGroup';
import './Home.scss';
import { ServersMap } from '../servers/data';
import { ShlinkLogo } from './img/ShlinkLogo';
export interface HomeProps {
servers: ServersMap;
}
const Home = ({ servers }: HomeProps) => {
const serversList = values(servers);
const hasServers = !isEmpty(serversList);
return (
<div className="home">
<Card className="home__main-card">
<Row noGutters>
<div className="col-md-5 d-none d-md-block">
<div className="p-4">
<ShlinkLogo />
</div>
</div>
<div className="col-md-7 home__servers-container">
<div className="p-4">
<h1 className="home__title">Welcome!</h1>
</div>
<ServersListGroup embedded servers={serversList}>
{!hasServers && (
<div className="p-4">
<p>This application will help you to manage your Shlink servers.</p>
<p>To start, please, <Link to="/server/create">add your first server</Link>.</p>
<p className="m-0">
You still don&lsquo;t have a Shlink server?
Learn how to <ExternalLink href="https://shlink.io/documentation">get started</ExternalLink>.
</p>
</div>
)}
</ServersListGroup>
</div>
</Row>
</Card>
</div>
);
};
export default Home;

View File

@@ -1,65 +0,0 @@
import { faPlus as plusIcon, faChevronDown as arrowIcon } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import React from 'react';
import { Link } from 'react-router-dom';
import { Collapse, Nav, Navbar, NavbarBrand, NavbarToggler, NavItem, NavLink } from 'reactstrap';
import classnames from 'classnames';
import PropTypes from 'prop-types';
import shlinkLogo from './shlink-logo-white.png';
import './MainHeader.scss';
const MainHeader = (ServersDropdown) => class MainHeader extends React.Component {
static propTypes = {
location: PropTypes.object,
};
state = { isOpen: false };
handleToggle = () => {
this.setState(({ isOpen }) => ({
isOpen: !isOpen,
}));
};
componentDidUpdate(prevProps) {
if (this.props.location !== prevProps.location) {
this.setState({ isOpen: false });
}
}
render() {
const { location } = this.props;
const createServerPath = '/server/create';
const toggleClass = classnames('main-header__toggle-icon', {
'main-header__toggle-icon--opened': this.state.isOpen,
});
return (
<Navbar color="primary" dark fixed="top" className="main-header" expand="md">
<NavbarBrand tag={Link} to="/">
<img src={shlinkLogo} alt="Shlink" className="main-header__brand-logo" /> Shlink
</NavbarBrand>
<NavbarToggler onClick={this.handleToggle}>
<FontAwesomeIcon icon={arrowIcon} className={toggleClass} />
</NavbarToggler>
<Collapse navbar isOpen={this.state.isOpen}>
<Nav navbar className="ml-auto">
<NavItem>
<NavLink
tag={Link}
to={createServerPath}
active={location.pathname === createServerPath}
>
<FontAwesomeIcon icon={plusIcon} />&nbsp; Add server
</NavLink>
</NavItem>
<ServersDropdown />
</Nav>
</Collapse>
</Navbar>
);
}
};
export default MainHeader;

View File

@@ -1,8 +1,8 @@
@import '../utils/base';
.main-header.main-header {
background-color: $mainColor !important;
color: white;
background-color: var(--brand-color) !important;
.navbar-brand {
color: inherit !important;

45
src/common/MainHeader.tsx Normal file
View File

@@ -0,0 +1,45 @@
import { faChevronDown as arrowIcon, faCogs as cogsIcon } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { FC, useEffect } from 'react';
import { Link } from 'react-router-dom';
import { Collapse, Nav, Navbar, NavbarBrand, NavbarToggler, NavItem, NavLink } from 'reactstrap';
import classNames from 'classnames';
import { RouteComponentProps } from 'react-router';
import { useToggle } from '../utils/helpers/hooks';
import { ShlinkLogo } from './img/ShlinkLogo';
import './MainHeader.scss';
const MainHeader = (ServersDropdown: FC) => ({ location }: RouteComponentProps) => {
const [ isOpen, toggleOpen, , close ] = useToggle();
const { pathname } = location;
useEffect(close, [ location ]);
const settingsPath = '/settings';
const toggleClass = classNames('main-header__toggle-icon', { 'main-header__toggle-icon--opened': isOpen });
return (
<Navbar color="primary" dark fixed="top" className="main-header" expand="md">
<NavbarBrand tag={Link} to="/">
<ShlinkLogo className="main-header__brand-logo" color="white" /> Shlink
</NavbarBrand>
<NavbarToggler onClick={toggleOpen}>
<FontAwesomeIcon icon={arrowIcon} className={toggleClass} />
</NavbarToggler>
<Collapse navbar isOpen={isOpen}>
<Nav navbar className="ml-auto">
<NavItem>
<NavLink tag={Link} to={'/settings'} active={pathname === settingsPath}>
<FontAwesomeIcon icon={cogsIcon} />&nbsp; Settings
</NavLink>
</NavItem>
<ServersDropdown />
</Nav>
</Collapse>
</Navbar>
);
};
export default MainHeader;

View File

@@ -1,110 +0,0 @@
import React from 'react';
import { Route, Switch } from 'react-router-dom';
import { Swipeable } from 'react-swipeable';
import { faBars as burgerIcon } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import classnames from 'classnames';
import * as PropTypes from 'prop-types';
import { serverType } from '../servers/prop-types';
import NotFound from './NotFound';
import './MenuLayout.scss';
const MenuLayout = (TagsList, ShortUrls, AsideMenu, CreateShortUrl, ShortUrlVisits) =>
class MenuLayout extends React.Component {
static propTypes = {
match: PropTypes.object,
selectServer: PropTypes.func,
location: PropTypes.object,
selectedServer: serverType,
};
state = { showSideBar: false };
componentDidMount() {
const { match, selectServer } = this.props;
const { params: { serverId } } = match;
selectServer(serverId);
}
componentDidUpdate(prevProps) {
const { location } = this.props;
// Hide sidebar when location changes
if (location !== prevProps.location) {
this.setState({ showSideBar: false });
}
}
render() {
const { selectedServer, match } = this.props;
const { params: { serverId } } = match;
const burgerClasses = classnames('menu-layout__burger-icon', {
'menu-layout__burger-icon--active': this.state.showSideBar,
});
const swipeMenuIfNoModalExists = (showSideBar) => () => {
if (document.querySelector('.modal')) {
return;
}
this.setState({ showSideBar });
};
return (
<React.Fragment>
<FontAwesomeIcon
icon={burgerIcon}
className={burgerClasses}
onClick={() => this.setState(({ showSideBar }) => ({ showSideBar: !showSideBar }))}
/>
<Swipeable
delta={40}
className="menu-layout__swipeable"
onSwipedLeft={swipeMenuIfNoModalExists(false)}
onSwipedRight={swipeMenuIfNoModalExists(true)}
>
<div className="row menu-layout__swipeable-inner">
<AsideMenu
className="col-lg-2 col-md-3"
selectedServer={selectedServer}
showOnMobile={this.state.showSideBar}
/>
<div
className="col-lg-10 offset-lg-2 col-md-9 offset-md-3"
onClick={() => this.setState({ showSideBar: false })}
>
<Switch>
<Route
exact
path="/server/:serverId/list-short-urls/:page"
component={ShortUrls}
/>
<Route
exact
path="/server/:serverId/create-short-url"
component={CreateShortUrl}
/>
<Route
exact
path="/server/:serverId/short-code/:shortCode/visits"
component={ShortUrlVisits}
/>
<Route
exact
path="/server/:serverId/manage-tags"
component={TagsList}
/>
<Route
render={() => <NotFound to={`/server/${serverId}/list-short-urls/1`} btnText="List short URLs" />}
/>
</Switch>
</div>
</div>
</Swipeable>
</React.Fragment>
);
}
};
export default MenuLayout;

View File

@@ -32,3 +32,12 @@
.menu-layout__burger-icon--active {
color: white;
}
.menu-layout__container.menu-layout__container {
padding: 20px 0 0;
min-height: 100%;
@media (min-width: $mdMin) {
padding: 30px 0 0 $asideMenuWidth;
}
}

70
src/common/MenuLayout.tsx Normal file
View File

@@ -0,0 +1,70 @@
import { FC, useEffect } from 'react';
import { Redirect, Route, Switch } from 'react-router-dom';
import { faBars as burgerIcon } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import classNames from 'classnames';
import { withSelectedServer } from '../servers/helpers/withSelectedServer';
import { useSwipeable, useToggle } from '../utils/helpers/hooks';
import { supportsOrphanVisits, supportsTagVisits } from '../utils/helpers/features';
import { isReachableServer } from '../servers/data';
import NotFound from './NotFound';
import { AsideMenuProps } from './AsideMenu';
import './MenuLayout.scss';
const MenuLayout = (
TagsList: FC,
ShortUrls: FC,
AsideMenu: FC<AsideMenuProps>,
CreateShortUrl: FC,
ShortUrlVisits: FC,
TagVisits: FC,
OrphanVisits: FC,
ServerError: FC,
Overview: FC,
EditShortUrl: FC,
) => withSelectedServer(({ location, selectedServer }) => {
const [ sidebarVisible, toggleSidebar, showSidebar, hideSidebar ] = useToggle();
useEffect(() => hideSidebar(), [ location ]);
if (!isReachableServer(selectedServer)) {
return <ServerError />;
}
const addTagsVisitsRoute = supportsTagVisits(selectedServer);
const addOrphanVisitsRoute = supportsOrphanVisits(selectedServer);
const burgerClasses = classNames('menu-layout__burger-icon', { 'menu-layout__burger-icon--active': sidebarVisible });
const swipeableProps = useSwipeable(showSidebar, hideSidebar);
return (
<>
<FontAwesomeIcon icon={burgerIcon} className={burgerClasses} onClick={toggleSidebar} />
<div {...swipeableProps} className="menu-layout__swipeable">
<div className="menu-layout__swipeable-inner">
<AsideMenu selectedServer={selectedServer} showOnMobile={sidebarVisible} />
<div className="menu-layout__container" onClick={() => hideSidebar()}>
<div className="container-xl">
<Switch>
<Redirect exact from="/server/:serverId" to="/server/:serverId/overview" />
<Route exact path="/server/:serverId/overview" component={Overview} />
<Route exact path="/server/:serverId/list-short-urls/:page" component={ShortUrls} />
<Route exact path="/server/:serverId/create-short-url" component={CreateShortUrl} />
<Route path="/server/:serverId/short-code/:shortCode/visits" component={ShortUrlVisits} />
<Route path="/server/:serverId/short-code/:shortCode/edit" component={EditShortUrl} />
{addTagsVisitsRoute && <Route path="/server/:serverId/tag/:tag/visits" component={TagVisits} />}
{addOrphanVisitsRoute && <Route path="/server/:serverId/orphan-visits" component={OrphanVisits} />}
<Route exact path="/server/:serverId/manage-tags" component={TagsList} />
<Route
render={() => <NotFound to={`/server/${selectedServer.id}/list-short-urls/1`}>List short URLs</NotFound>}
/>
</Switch>
</div>
</div>
</div>
</div>
</>
);
}, ServerError);
export default MenuLayout;

View File

@@ -0,0 +1,9 @@
@import '../utils/base';
.no-menu-wrapper {
padding: 15px 0 0;
@media (min-width: $mdMin) {
padding: 30px 20px 20px;
}
}

View File

@@ -0,0 +1,6 @@
import { FC } from 'react';
import './NoMenuLayout.scss';
const NoMenuLayout: FC = ({ children }) => <div className="no-menu-wrapper container-xl">{children}</div>;
export default NoMenuLayout;

View File

@@ -1,23 +0,0 @@
import React from 'react';
import { Link } from 'react-router-dom';
import * as PropTypes from 'prop-types';
const propTypes = {
to: PropTypes.string,
btnText: PropTypes.string,
};
const NotFound = ({ to = '/', btnText = 'Home' }) => (
<div className="home">
<h2>Oops! We could not find requested route.</h2>
<p>
Use your browser{'\''}s back button to navigate to the page you have previously come from, or just press this button.
</p>
<br />
<Link to={to} className="btn btn-outline-primary btn-lg">{btnText}</Link>
</div>
);
NotFound.propTypes = propTypes;
export default NotFound;

23
src/common/NotFound.tsx Normal file
View File

@@ -0,0 +1,23 @@
import { FC } from 'react';
import { Link } from 'react-router-dom';
import { SimpleCard } from '../utils/SimpleCard';
interface NotFoundProps {
to?: string;
}
const NotFound: FC<NotFoundProps> = ({ to = '/', children = 'Home' }) => (
<div className="home">
<SimpleCard className="p-4">
<h2>Oops! We could not find requested route.</h2>
<p>
Use your browser&apos;s back button to navigate to the page you have previously come from, or just press this
button.
</p>
<br />
<Link to={to} className="btn btn-outline-primary btn-lg">{children}</Link>
</SimpleCard>
</div>
);
export default NotFound;

View File

@@ -1,23 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
const ScrollToTop = ({ scrollTo }) => class ScrollToTop extends React.Component {
static propTypes = {
location: PropTypes.object,
children: PropTypes.node,
};
componentDidUpdate({ location: prevLocation }) {
const { location } = this.props;
if (location !== prevLocation) {
scrollTo(0, 0);
}
}
render() {
return this.props.children;
}
};
export default ScrollToTop;

View File

@@ -0,0 +1,12 @@
import { PropsWithChildren, useEffect } from 'react';
import { RouteComponentProps } from 'react-router';
const ScrollToTop = () => ({ location, children }: PropsWithChildren<RouteComponentProps>) => {
useEffect(() => {
scrollTo(0, 0);
}, [ location ]);
return <>{children}</>;
};
export default ScrollToTop;

View File

@@ -0,0 +1,33 @@
import { pipe } from 'ramda';
import { ExternalLink } from 'react-external-link';
import { versionToPrintable, versionToSemVer } from '../utils/helpers/version';
import { isReachableServer } from '../servers/data';
import { ShlinkVersionsContainerProps } from './ShlinkVersionsContainer';
const SHLINK_WEB_CLIENT_VERSION = '%_VERSION_%';
const normalizeVersion = pipe(versionToSemVer(), versionToPrintable);
export interface ShlinkVersionsProps extends ShlinkVersionsContainerProps {
clientVersion?: string;
}
const VersionLink = ({ project, version }: { project: 'shlink' | 'shlink-web-client'; version: string }) => (
<ExternalLink href={`https://github.com/shlinkio/${project}/releases/${version}`} className="text-muted">
<b>{version}</b>
</ExternalLink>
);
const ShlinkVersions = ({ selectedServer, clientVersion = SHLINK_WEB_CLIENT_VERSION }: ShlinkVersionsProps) => {
const normalizedClientVersion = normalizeVersion(clientVersion);
return (
<small className="text-muted">
{isReachableServer(selectedServer) &&
<>Server: <VersionLink project="shlink" version={selectedServer.printableVersion} /> - </>
}
Client: <VersionLink project="shlink-web-client" version={normalizedClientVersion} />
</small>
);
};
export default ShlinkVersions;

View File

@@ -0,0 +1,9 @@
@import '../utils/base';
.shlink-versions-container--with-server {
margin-left: 0;
@media (min-width: $mdMin) {
margin-left: $asideMenuWidth;
}
}

View File

@@ -0,0 +1,22 @@
import classNames from 'classnames';
import { isReachableServer, SelectedServer } from '../servers/data';
import ShlinkVersions from './ShlinkVersions';
import './ShlinkVersionsContainer.scss';
export interface ShlinkVersionsContainerProps {
selectedServer: SelectedServer;
}
const ShlinkVersionsContainer = ({ selectedServer }: ShlinkVersionsContainerProps) => {
const classes = classNames('text-center', {
'shlink-versions-container--with-server': isReachableServer(selectedServer),
});
return (
<div className={classes}>
<ShlinkVersions selectedServer={selectedServer} />
</div>
);
};
export default ShlinkVersionsContainer;

View File

@@ -1,65 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Pagination, PaginationItem, PaginationLink } from 'reactstrap';
import { range, max, min } from 'ramda';
import './SimplePaginator.scss';
const propTypes = {
pagesCount: PropTypes.number.isRequired,
currentPage: PropTypes.number.isRequired,
setCurrentPage: PropTypes.func.isRequired,
};
export const ellipsis = '...';
const pagination = (currentPage, pageCount) => {
const delta = 2;
const pages = range(
max(delta, currentPage - delta),
min(pageCount - 1, currentPage + delta) + 1
);
if (currentPage - delta > delta) {
pages.unshift(ellipsis);
}
if (currentPage + delta < pageCount - 1) {
pages.push(ellipsis);
}
pages.unshift(1);
pages.push(pageCount);
return pages;
};
const SimplePaginator = ({ pagesCount, currentPage, setCurrentPage }) => {
if (pagesCount < 2) {
return null;
}
const onClick = (page) => () => setCurrentPage(page);
return (
<Pagination listClassName="flex-wrap justify-content-center mb-0 simple-paginator">
<PaginationItem disabled={currentPage <= 1}>
<PaginationLink previous tag="span" onClick={onClick(currentPage - 1)} />
</PaginationItem>
{pagination(currentPage, pagesCount).map((page, index) => (
<PaginationItem
key={page !== ellipsis ? page : `${page}_${index}`}
active={page === currentPage}
disabled={page === ellipsis}
>
<PaginationLink tag="span" onClick={onClick(page)}>{page}</PaginationLink>
</PaginationItem>
))}
<PaginationItem disabled={currentPage >= pagesCount}>
<PaginationLink next tag="span" onClick={onClick(currentPage + 1)} />
</PaginationItem>
</Pagination>
);
};
SimplePaginator.propTypes = propTypes;
export default SimplePaginator;

Some files were not shown because too many files have changed in this diff Show More