feature: remove sidebar & redesign top navbar
This commit is contained in:
@ -3,6 +3,7 @@
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@apollo/client": "^3.0.0-rc.8",
|
||||
"@apollo/react-common": "^3.1.4",
|
||||
"@apollo/react-hooks": "^3.1.3",
|
||||
"@fortawesome/fontawesome-svg-core": "^1.2.27",
|
||||
|
471
web/report.20200621.183857.68808.0.001.json
Normal file
471
web/report.20200621.183857.68808.0.001.json
Normal file
@ -0,0 +1,471 @@
|
||||
|
||||
{
|
||||
"header": {
|
||||
"reportVersion": 1,
|
||||
"event": "Allocation failed - JavaScript heap out of memory",
|
||||
"trigger": "FatalError",
|
||||
"filename": "report.20200621.183857.68808.0.001.json",
|
||||
"dumpEventTime": "2020-06-21T18:38:57Z",
|
||||
"dumpEventTimeStamp": "1592782737343",
|
||||
"processId": 68808,
|
||||
"cwd": "/home/jordan/Projects/project-citadel/web",
|
||||
"commandLine": [
|
||||
"/usr/bin/node",
|
||||
"/home/jordan/.config/coc/extensions/node_modules/coc-tsserver/bin/tsserverForkStart",
|
||||
"/home/jordan/Projects/project-citadel/web/node_modules/typescript/lib/tsserver.js",
|
||||
"--allowLocalPluginLoads",
|
||||
"--useInferredProjectPerProjectRoot",
|
||||
"--cancellationPipeName",
|
||||
"/tmp/coc-nvim-tscancellation-49e6b7aafa04f6c486f4.sock*",
|
||||
"--npmLocation",
|
||||
"\"/usr/bin/npm\"",
|
||||
"--noGetErrOnBackgroundUpdate",
|
||||
"--validateDefaultNpmLocation"
|
||||
],
|
||||
"nodejsVersion": "v13.8.0",
|
||||
"glibcVersionRuntime": "2.30",
|
||||
"glibcVersionCompiler": "2.30",
|
||||
"wordSize": 64,
|
||||
"arch": "x64",
|
||||
"platform": "linux",
|
||||
"componentVersions": {
|
||||
"node": "13.8.0",
|
||||
"v8": "7.9.317.25-node.28",
|
||||
"uv": "1.34.1",
|
||||
"zlib": "1.2.11",
|
||||
"brotli": "1.0.7",
|
||||
"ares": "1.15.0",
|
||||
"modules": "79",
|
||||
"nghttp2": "1.39.2",
|
||||
"napi": "5",
|
||||
"llhttp": "2.0.4",
|
||||
"openssl": "1.1.1d",
|
||||
"cldr": "36.0",
|
||||
"icu": "65.1",
|
||||
"tz": "2019c",
|
||||
"unicode": "12.1"
|
||||
},
|
||||
"release": {
|
||||
"name": "node",
|
||||
"headersUrl": "https://nodejs.org/download/release/v13.8.0/node-v13.8.0-headers.tar.gz",
|
||||
"sourceUrl": "https://nodejs.org/download/release/v13.8.0/node-v13.8.0.tar.gz"
|
||||
},
|
||||
"osName": "Linux",
|
||||
"osRelease": "4.19.101-1-lts",
|
||||
"osVersion": "#1 SMP Sat, 01 Feb 2020 16:35:36 +0000",
|
||||
"osMachine": "x86_64",
|
||||
"cpus": [
|
||||
{
|
||||
"model": "Intel(R) Core(TM) i7-6500U CPU @ 2.50GHz",
|
||||
"speed": 2646,
|
||||
"user": 48577700,
|
||||
"nice": 14200,
|
||||
"sys": 8014100,
|
||||
"idle": 166454900,
|
||||
"irq": 735500
|
||||
},
|
||||
{
|
||||
"model": "Intel(R) Core(TM) i7-6500U CPU @ 2.50GHz",
|
||||
"speed": 2646,
|
||||
"user": 48745100,
|
||||
"nice": 19200,
|
||||
"sys": 7840700,
|
||||
"idle": 42737200,
|
||||
"irq": 528800
|
||||
},
|
||||
{
|
||||
"model": "Intel(R) Core(TM) i7-6500U CPU @ 2.50GHz",
|
||||
"speed": 2647,
|
||||
"user": 48543900,
|
||||
"nice": 15800,
|
||||
"sys": 7804900,
|
||||
"idle": 42914200,
|
||||
"irq": 530200
|
||||
},
|
||||
{
|
||||
"model": "Intel(R) Core(TM) i7-6500U CPU @ 2.50GHz",
|
||||
"speed": 2726,
|
||||
"user": 48996200,
|
||||
"nice": 19900,
|
||||
"sys": 7808300,
|
||||
"idle": 42957300,
|
||||
"irq": 390800
|
||||
}
|
||||
],
|
||||
"networkInterfaces": [
|
||||
{
|
||||
"name": "lo",
|
||||
"internal": true,
|
||||
"mac": "00:00:00:00:00:00",
|
||||
"address": "127.0.0.1",
|
||||
"netmask": "255.0.0.0",
|
||||
"family": "IPv4"
|
||||
},
|
||||
{
|
||||
"name": "wlp3s0",
|
||||
"internal": false,
|
||||
"mac": "7c:b0:c2:fe:93:86",
|
||||
"address": "192.168.43.5",
|
||||
"netmask": "255.255.255.0",
|
||||
"family": "IPv4"
|
||||
},
|
||||
{
|
||||
"name": "docker0",
|
||||
"internal": false,
|
||||
"mac": "02:42:13:a9:89:9d",
|
||||
"address": "172.17.0.1",
|
||||
"netmask": "255.255.0.0",
|
||||
"family": "IPv4"
|
||||
},
|
||||
{
|
||||
"name": "br-e929893879ec",
|
||||
"internal": false,
|
||||
"mac": "02:42:48:f4:23:30",
|
||||
"address": "172.19.0.1",
|
||||
"netmask": "255.255.0.0",
|
||||
"family": "IPv4"
|
||||
},
|
||||
{
|
||||
"name": "lo",
|
||||
"internal": true,
|
||||
"mac": "00:00:00:00:00:00",
|
||||
"address": "::1",
|
||||
"netmask": "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
|
||||
"family": "IPv6",
|
||||
"scopeid": 0
|
||||
},
|
||||
{
|
||||
"name": "wlp3s0",
|
||||
"internal": false,
|
||||
"mac": "7c:b0:c2:fe:93:86",
|
||||
"address": "2600:100b:b000:9bf1:7eb0:c2ff:fefe:9386",
|
||||
"netmask": "ffff:ffff:ffff:ffff::",
|
||||
"family": "IPv6",
|
||||
"scopeid": 0
|
||||
},
|
||||
{
|
||||
"name": "wlp3s0",
|
||||
"internal": false,
|
||||
"mac": "7c:b0:c2:fe:93:86",
|
||||
"address": "fe80::7eb0:c2ff:fefe:9386",
|
||||
"netmask": "ffff:ffff:ffff:ffff::",
|
||||
"family": "IPv6",
|
||||
"scopeid": 3
|
||||
},
|
||||
{
|
||||
"name": "docker0",
|
||||
"internal": false,
|
||||
"mac": "02:42:13:a9:89:9d",
|
||||
"address": "fe80::42:13ff:fea9:899d",
|
||||
"netmask": "ffff:ffff:ffff:ffff::",
|
||||
"family": "IPv6",
|
||||
"scopeid": 5
|
||||
},
|
||||
{
|
||||
"name": "veth79cfd83",
|
||||
"internal": false,
|
||||
"mac": "a2:e9:86:a8:58:bb",
|
||||
"address": "fe80::a0e9:86ff:fea8:58bb",
|
||||
"netmask": "ffff:ffff:ffff:ffff::",
|
||||
"family": "IPv6",
|
||||
"scopeid": 7
|
||||
},
|
||||
{
|
||||
"name": "br-e929893879ec",
|
||||
"internal": false,
|
||||
"mac": "02:42:48:f4:23:30",
|
||||
"address": "fe80::42:48ff:fef4:2330",
|
||||
"netmask": "ffff:ffff:ffff:ffff::",
|
||||
"family": "IPv6",
|
||||
"scopeid": 8
|
||||
},
|
||||
{
|
||||
"name": "vethe27cc3e",
|
||||
"internal": false,
|
||||
"mac": "c6:bd:d7:3b:6c:96",
|
||||
"address": "fe80::c4bd:d7ff:fe3b:6c96",
|
||||
"netmask": "ffff:ffff:ffff:ffff::",
|
||||
"family": "IPv6",
|
||||
"scopeid": 10
|
||||
}
|
||||
],
|
||||
"host": "archlinux"
|
||||
},
|
||||
"javascriptStack": {
|
||||
"message": "No stack.",
|
||||
"stack": [
|
||||
"Unavailable."
|
||||
]
|
||||
},
|
||||
"nativeStack": [
|
||||
{
|
||||
"pc": "0x0000557aee1dae8a",
|
||||
"symbol": "report::TriggerNodeReport(v8::Isolate*, node::Environment*, char const*, char const*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, v8::Local<v8::String>) [/usr/bin/node]"
|
||||
},
|
||||
{
|
||||
"pc": "0x0000557aee09bd48",
|
||||
"symbol": "node::OnFatalError(char const*, char const*) [/usr/bin/node]"
|
||||
},
|
||||
{
|
||||
"pc": "0x0000557aee20f382",
|
||||
"symbol": "v8::Utils::ReportOOMFailure(v8::internal::Isolate*, char const*, bool) [/usr/bin/node]"
|
||||
},
|
||||
{
|
||||
"pc": "0x0000557aee20f5e8",
|
||||
"symbol": "v8::internal::V8::FatalProcessOutOfMemory(v8::internal::Isolate*, char const*, bool) [/usr/bin/node]"
|
||||
},
|
||||
{
|
||||
"pc": "0x0000557aee399906",
|
||||
"symbol": " [/usr/bin/node]"
|
||||
},
|
||||
{
|
||||
"pc": "0x0000557aee399a49",
|
||||
"symbol": " [/usr/bin/node]"
|
||||
},
|
||||
{
|
||||
"pc": "0x0000557aee3ac22d",
|
||||
"symbol": "v8::internal::Heap::PerformGarbageCollection(v8::internal::GarbageCollector, v8::GCCallbackFlags) [/usr/bin/node]"
|
||||
},
|
||||
{
|
||||
"pc": "0x0000557aee3acf58",
|
||||
"symbol": "v8::internal::Heap::CollectGarbage(v8::internal::AllocationSpace, v8::internal::GarbageCollectionReason, v8::GCCallbackFlags) [/usr/bin/node]"
|
||||
},
|
||||
{
|
||||
"pc": "0x0000557aee3af48c",
|
||||
"symbol": "v8::internal::Heap::AllocateRawWithLightRetrySlowPath(int, v8::internal::AllocationType, v8::internal::AllocationOrigin, v8::internal::AllocationAlignment) [/usr/bin/node]"
|
||||
},
|
||||
{
|
||||
"pc": "0x0000557aee3af4f4",
|
||||
"symbol": "v8::internal::Heap::AllocateRawWithRetryOrFailSlowPath(int, v8::internal::AllocationType, v8::internal::AllocationOrigin, v8::internal::AllocationAlignment) [/usr/bin/node]"
|
||||
},
|
||||
{
|
||||
"pc": "0x0000557aee37499b",
|
||||
"symbol": "v8::internal::Factory::NewFillerObject(int, bool, v8::internal::AllocationType, v8::internal::AllocationOrigin) [/usr/bin/node]"
|
||||
},
|
||||
{
|
||||
"pc": "0x0000557aee6a5540",
|
||||
"symbol": "v8::internal::Runtime_AllocateInYoungGeneration(int, unsigned long*, v8::internal::Isolate*) [/usr/bin/node]"
|
||||
},
|
||||
{
|
||||
"pc": "0x0000557aee9febd9",
|
||||
"symbol": " [/usr/bin/node]"
|
||||
}
|
||||
],
|
||||
"javascriptHeap": {
|
||||
"totalMemory": 2152407040,
|
||||
"totalCommittedMemory": 2150546232,
|
||||
"usedMemory": 2137632424,
|
||||
"availableMemory": 47662336,
|
||||
"memoryLimit": 2197815296,
|
||||
"heapSpaces": {
|
||||
"read_only_space": {
|
||||
"memorySize": 262144,
|
||||
"committedMemory": 33328,
|
||||
"capacity": 33040,
|
||||
"used": 33040,
|
||||
"available": 0
|
||||
},
|
||||
"new_space": {
|
||||
"memorySize": 2097152,
|
||||
"committedMemory": 1029144,
|
||||
"capacity": 1047424,
|
||||
"used": 21136,
|
||||
"available": 1026288
|
||||
},
|
||||
"old_space": {
|
||||
"memorySize": 2058231808,
|
||||
"committedMemory": 2057888048,
|
||||
"capacity": 2048399928,
|
||||
"used": 2048219560,
|
||||
"available": 180368
|
||||
},
|
||||
"code_space": {
|
||||
"memorySize": 9080832,
|
||||
"committedMemory": 8864320,
|
||||
"capacity": 7755264,
|
||||
"used": 7755264,
|
||||
"available": 0
|
||||
},
|
||||
"map_space": {
|
||||
"memorySize": 1052672,
|
||||
"committedMemory": 1048960,
|
||||
"capacity": 740080,
|
||||
"used": 740080,
|
||||
"available": 0
|
||||
},
|
||||
"large_object_space": {
|
||||
"memorySize": 81305600,
|
||||
"committedMemory": 81305600,
|
||||
"capacity": 80548528,
|
||||
"used": 80548528,
|
||||
"available": 0
|
||||
},
|
||||
"code_large_object_space": {
|
||||
"memorySize": 376832,
|
||||
"committedMemory": 376832,
|
||||
"capacity": 314816,
|
||||
"used": 314816,
|
||||
"available": 0
|
||||
},
|
||||
"new_large_object_space": {
|
||||
"memorySize": 0,
|
||||
"committedMemory": 0,
|
||||
"capacity": 1047424,
|
||||
"used": 0,
|
||||
"available": 1047424
|
||||
}
|
||||
}
|
||||
},
|
||||
"resourceUsage": {
|
||||
"userCpuSeconds": 3484.11,
|
||||
"kernelCpuSeconds": 64.7409,
|
||||
"cpuConsumptionPercent": 35.606,
|
||||
"maxRss": 2330890240,
|
||||
"pageFaults": {
|
||||
"IORequired": 31,
|
||||
"IONotRequired": 9729381
|
||||
},
|
||||
"fsActivity": {
|
||||
"reads": 53144,
|
||||
"writes": 16
|
||||
}
|
||||
},
|
||||
"uvthreadResourceUsage": {
|
||||
"userCpuSeconds": 2308.89,
|
||||
"kernelCpuSeconds": 31.2857,
|
||||
"cpuConsumptionPercent": 23.4792,
|
||||
"fsActivity": {
|
||||
"reads": 53248,
|
||||
"writes": 16
|
||||
}
|
||||
},
|
||||
"libuv": [
|
||||
],
|
||||
"environmentVariables": {
|
||||
"COLORTERM": "truecolor",
|
||||
"DBUS_SESSION_BUS_ADDRESS": "unix:path=/run/user/1000/bus",
|
||||
"DESKTOP_SESSION": "awesome",
|
||||
"DISPLAY": ":0.0",
|
||||
"EDITOR": "nano",
|
||||
"FZF_DEFAULT_COMMAND": "ag --hidden --ignore .git -g \"\"",
|
||||
"GDMSESSION": "awesome",
|
||||
"GO111MODULE": "on",
|
||||
"GOBIN": "/home/jordan/go/bin",
|
||||
"GREP_COLOR": "37;45",
|
||||
"GREP_COLORS": "mt=37;45",
|
||||
"GTK_MODULES": "canberra-gtk-module",
|
||||
"HOME": "/home/jordan",
|
||||
"LANG": "C",
|
||||
"LESS": "-F -g -i -M -R -S -w -X -z-4",
|
||||
"LESS_TERMCAP_mb": "\u001b[01;31m",
|
||||
"LESS_TERMCAP_md": "\u001b[01;31m",
|
||||
"LESS_TERMCAP_me": "\u001b[0m",
|
||||
"LESS_TERMCAP_se": "\u001b[0m",
|
||||
"LESS_TERMCAP_so": "\u001b[00;47;30m",
|
||||
"LESS_TERMCAP_ue": "\u001b[0m",
|
||||
"LESS_TERMCAP_us": "\u001b[01;32m",
|
||||
"LOGNAME": "jordan",
|
||||
"LS_COLORS": "rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=00:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.zst=01;31:*.tzst=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.wim=01;31:*.swm=01;31:*.dwm=01;31:*.esd=01;31:*.jpg=01;35:*.jpeg=01;35:*.mjpg=01;35:*.mjpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.m4a=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.oga=00;36:*.opus=00;36:*.spx=00;36:*.xspf=00;36:",
|
||||
"MAIL": "/var/spool/mail/jordan",
|
||||
"OLDPWD": "/home/jordan/Projects/project-citadel/web",
|
||||
"PAGER": "less",
|
||||
"PATH": "/home/jordan/.local/bin:/usr/local/bin:/usr/local/sbin:/home/nightwolf/Programs/cmake/bin:/home/nightwolf/Programs/idea-IU-163.13906.18/bin:/home/nightwolf/Programs/wpcli:/home/nightwolf/neovim/bin:/home/nightwolf/Programs/Postman:/home/nightwolf/Programs/Android_SDK/tools/bin:/home/nightwolf/Development/PhantomJS/bin:/home/nightwolf/Programs/node/bin:/home/nightwolf/pyenv/bin:/home/nightwolf/Programs/vv:/usr/bin:/bin:/usr/lib/jvm/default/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl:/usr/local/go/bin:/home/jordan/go/bin:/home/jordan/.garden/bin:~/Programs/node/bin:~/.utilities:/home/jordan/.fzf/bin:/home/jordan/.gem/ruby/2.6.0/bin:/home/jordan/.garden/bin:~/Programs/node/bin:~/.utilities:/home/jordan/.gem/ruby/2.6.0/bin",
|
||||
"PWD": "/home/jordan/Projects/project-citadel/web",
|
||||
"SHELL": "/usr/bin/zsh",
|
||||
"SHLVL": "2",
|
||||
"SSH_AGENT_PID": "773",
|
||||
"SSH_AUTH_SOCK": "/tmp/ssh-agent.sock.1000",
|
||||
"TERM": "xterm-256color",
|
||||
"TMUX": "/tmp/tmux-1000/default,13203,1",
|
||||
"TMUX_PANE": "%2",
|
||||
"USER": "jordan",
|
||||
"VIRTUALENVWRAPPER_PYTHON": "/usr/bin/python3",
|
||||
"VIRTUAL_ENV_DISABLE_PROMPT": "12",
|
||||
"VISUAL": "nano",
|
||||
"VTE_VERSION": "5602",
|
||||
"WINDOWID": "6291459",
|
||||
"WORKON_HOME": "/home/jordan/.virtualenvs",
|
||||
"XAUTHORITY": "/home/jordan/.Xauthority",
|
||||
"XDG_GREETER_DATA_DIR": "/var/lib/lightdm-data/jordan",
|
||||
"XDG_RUNTIME_DIR": "/run/user/1000",
|
||||
"XDG_SEAT": "seat0",
|
||||
"XDG_SEAT_PATH": "/org/freedesktop/DisplayManager/Seat0",
|
||||
"XDG_SESSION_CLASS": "user",
|
||||
"XDG_SESSION_DESKTOP": "awesome",
|
||||
"XDG_SESSION_ID": "1",
|
||||
"XDG_SESSION_PATH": "/org/freedesktop/DisplayManager/Session0",
|
||||
"XDG_SESSION_TYPE": "x11",
|
||||
"XDG_VTNR": "7",
|
||||
"_": "/usr/bin/nvim",
|
||||
"is_vim": "ps -o state= -o comm= -t '#{pane_tty}' | grep -iqE '^[^TXZ ]+ +(\\S+\\/)?g?(view|n?vim?x?)(diff)?$'",
|
||||
"tmux_version": "$(tmux -V | sed -En \"s/^tmux ([0-9]+(.[0-9]+)?).*/\\1/p\")",
|
||||
"LC_MESSAGES": "",
|
||||
"VIMRUNTIME": "/usr/share/nvim/runtime",
|
||||
"NVIM_LISTEN_ADDRESS": "/tmp/nvimaHxiZq/0",
|
||||
"MYVIMRC": "/home/jordan/.config/nvim/init.vim",
|
||||
"COC_VIMCONFIG": "/home/jordan/.config/nvim",
|
||||
"COC_DATA_HOME": "/home/jordan/.config/coc",
|
||||
"TSS_LOG": "-level verbose -file /tmp/coc-nvim-tsc.log",
|
||||
"NODE_PATH": "/home/jordan/Projects/project-citadel/web/node_modules"
|
||||
},
|
||||
"userLimits": {
|
||||
"core_file_size_blocks": {
|
||||
"soft": "unlimited",
|
||||
"hard": "unlimited"
|
||||
},
|
||||
"data_seg_size_kbytes": {
|
||||
"soft": "unlimited",
|
||||
"hard": "unlimited"
|
||||
},
|
||||
"file_size_blocks": {
|
||||
"soft": "unlimited",
|
||||
"hard": "unlimited"
|
||||
},
|
||||
"max_locked_memory_bytes": {
|
||||
"soft": 65536,
|
||||
"hard": 65536
|
||||
},
|
||||
"max_memory_size_kbytes": {
|
||||
"soft": "unlimited",
|
||||
"hard": "unlimited"
|
||||
},
|
||||
"open_files": {
|
||||
"soft": 524288,
|
||||
"hard": 524288
|
||||
},
|
||||
"stack_size_bytes": {
|
||||
"soft": 8388608,
|
||||
"hard": "unlimited"
|
||||
},
|
||||
"cpu_time_seconds": {
|
||||
"soft": "unlimited",
|
||||
"hard": "unlimited"
|
||||
},
|
||||
"max_user_processes": {
|
||||
"soft": 31138,
|
||||
"hard": 31138
|
||||
},
|
||||
"virtual_memory_kbytes": {
|
||||
"soft": "unlimited",
|
||||
"hard": "unlimited"
|
||||
}
|
||||
},
|
||||
"sharedObjects": [
|
||||
"linux-vdso.so.1",
|
||||
"/usr/lib/libz.so.1",
|
||||
"/usr/lib/libcares.so.2",
|
||||
"/usr/lib/libnghttp2.so.14",
|
||||
"/usr/lib/libcrypto.so.1.1",
|
||||
"/usr/lib/libssl.so.1.1",
|
||||
"/usr/lib/libicui18n.so.65",
|
||||
"/usr/lib/libicuuc.so.65",
|
||||
"/usr/lib/libdl.so.2",
|
||||
"/usr/lib/libstdc++.so.6",
|
||||
"/usr/lib/libm.so.6",
|
||||
"/usr/lib/libgcc_s.so.1",
|
||||
"/usr/lib/libpthread.so.0",
|
||||
"/usr/lib/libc.so.6",
|
||||
"/usr/lib/libicudata.so.65",
|
||||
"/lib64/ld-linux-x86-64.so.2"
|
||||
]
|
||||
}
|
158
web/src/Admin/index.tsx
Normal file
158
web/src/Admin/index.tsx
Normal file
@ -0,0 +1,158 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import Admin from 'shared/components/Admin';
|
||||
import GlobalTopNavbar from 'App/TopNavbar';
|
||||
import { useUsersQuery, useCreateUserAccountMutation, UsersDocument } from 'shared/generated/graphql';
|
||||
import Input from 'shared/components/Input';
|
||||
import styled from 'styled-components';
|
||||
import Button from 'shared/components/Button';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { usePopup, Popup } from 'shared/components/PopupMenu';
|
||||
import produce from 'immer';
|
||||
|
||||
type CreateUserData = {
|
||||
email: string;
|
||||
username: string;
|
||||
fullName: string;
|
||||
initials: string;
|
||||
password: string;
|
||||
};
|
||||
const CreateUserForm = styled.form`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
`;
|
||||
const CreateUserButton = styled(Button)`
|
||||
margin-top: 8px;
|
||||
padding: 6px 12px;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const AddUserInput = styled(Input)`
|
||||
margin-bottom: 8px;
|
||||
`;
|
||||
|
||||
const InputError = styled.span`
|
||||
color: rgba(${props => props.theme.colors.danger});
|
||||
font-size: 12px;
|
||||
`;
|
||||
type AddUserPopupProps = {
|
||||
onAddUser: (user: CreateUserData) => void;
|
||||
};
|
||||
const AddUserPopup: React.FC<AddUserPopupProps> = ({ onAddUser }) => {
|
||||
const { register, handleSubmit, errors } = useForm<CreateUserData>();
|
||||
const createUser = (data: CreateUserData) => {
|
||||
onAddUser(data);
|
||||
};
|
||||
return (
|
||||
<CreateUserForm onSubmit={handleSubmit(createUser)}>
|
||||
<AddUserInput
|
||||
floatingLabel
|
||||
width="100%"
|
||||
label="Full Name"
|
||||
id="fullName"
|
||||
name="fullName"
|
||||
variant="alternate"
|
||||
ref={register({ required: 'Full name is required' })}
|
||||
/>
|
||||
{errors.fullName && <InputError>{errors.fullName.message}</InputError>}
|
||||
<AddUserInput
|
||||
floatingLabel
|
||||
width="100%"
|
||||
label="Email"
|
||||
id="email"
|
||||
name="email"
|
||||
variant="alternate"
|
||||
ref={register({ required: 'Email is required' })}
|
||||
/>
|
||||
{errors.email && <InputError>{errors.email.message}</InputError>}
|
||||
<AddUserInput
|
||||
floatingLabel
|
||||
width="100%"
|
||||
label="Username"
|
||||
id="username"
|
||||
name="username"
|
||||
variant="alternate"
|
||||
ref={register({ required: 'Username is required' })}
|
||||
/>
|
||||
{errors.username && <InputError>{errors.username.message}</InputError>}
|
||||
<AddUserInput
|
||||
floatingLabel
|
||||
width="100%"
|
||||
label="Initials"
|
||||
id="initials"
|
||||
name="initials"
|
||||
variant="alternate"
|
||||
ref={register({ required: 'Initials is required' })}
|
||||
/>
|
||||
{errors.initials && <InputError>{errors.initials.message}</InputError>}
|
||||
<AddUserInput
|
||||
floatingLabel
|
||||
width="100%"
|
||||
label="Password"
|
||||
id="password"
|
||||
name="password"
|
||||
variant="alternate"
|
||||
ref={register({ required: 'Password is required' })}
|
||||
/>
|
||||
{errors.password && <InputError>{errors.password.message}</InputError>}
|
||||
<CreateUserButton type="submit">Create</CreateUserButton>
|
||||
</CreateUserForm>
|
||||
);
|
||||
};
|
||||
|
||||
const AdminRoute = () => {
|
||||
useEffect(() => {
|
||||
document.title = 'Citadel | Admin';
|
||||
}, []);
|
||||
const { loading, data } = useUsersQuery();
|
||||
const { showPopup, hidePopup } = usePopup();
|
||||
const [createUser] = useCreateUserAccountMutation({
|
||||
update: (client, createData) => {
|
||||
const cacheData: any = client.readQuery({
|
||||
query: UsersDocument,
|
||||
});
|
||||
console.log(cacheData);
|
||||
console.log(createData);
|
||||
const newData = produce(cacheData, (draftState: any) => {
|
||||
draftState.users = [...draftState.users, { ...createData.data.createUserAccount }];
|
||||
});
|
||||
|
||||
client.writeQuery({
|
||||
query: UsersDocument,
|
||||
data: {
|
||||
...newData,
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
if (loading) {
|
||||
return <GlobalTopNavbar projectID={null} onSaveProjectName={() => {}} name={null} />;
|
||||
}
|
||||
if (data) {
|
||||
return (
|
||||
<>
|
||||
<GlobalTopNavbar projectID={null} onSaveProjectName={() => {}} name={null} />
|
||||
<Admin
|
||||
initialTab={1}
|
||||
users={data.users.map((user: any) => ({ ...user, role: 'TBD' }))}
|
||||
onInviteUser={() => {}}
|
||||
onAddUser={$target => {
|
||||
showPopup(
|
||||
$target,
|
||||
<Popup tab={0} title="Add member" onClose={() => hidePopup()}>
|
||||
<AddUserPopup
|
||||
onAddUser={user => {
|
||||
createUser({ variables: { ...user } });
|
||||
hidePopup();
|
||||
}}
|
||||
/>
|
||||
</Popup>,
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
return <span>error</span>;
|
||||
};
|
||||
|
||||
export default AdminRoute;
|
@ -15,7 +15,7 @@ const GlobalNavbar = () => {
|
||||
<ButtonContainer>
|
||||
<Link to="/">
|
||||
<ActionButton name="Home">
|
||||
<Home size={28} color="#c2c6dc" />
|
||||
<Home width={28} height={28} />
|
||||
</ActionButton>
|
||||
</Link>
|
||||
<Link to="/projects">
|
||||
|
@ -3,14 +3,16 @@ import { Router, Switch, Route } from 'react-router-dom';
|
||||
import * as H from 'history';
|
||||
|
||||
import Dashboard from 'Dashboard';
|
||||
import Admin from 'Admin';
|
||||
import Projects from 'Projects';
|
||||
import Project from 'Projects/Project';
|
||||
import Teams from 'Teams';
|
||||
import Login from 'Auth';
|
||||
import Profile from 'Profile';
|
||||
import styled from 'styled-components';
|
||||
|
||||
const MainContent = styled.div`
|
||||
padding: 0 0 0 80px;
|
||||
padding: 0 0 0 0;
|
||||
background: #262c49;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
@ -28,7 +30,9 @@ const Routes = ({ history }: RoutesProps) => (
|
||||
<Route exact path="/" component={Dashboard} />
|
||||
<Route exact path="/projects" component={Projects} />
|
||||
<Route path="/projects/:projectID" component={Project} />
|
||||
<Route path="/teams/:teamID" component={Teams} />
|
||||
<Route path="/profile" component={Profile} />
|
||||
<Route path="/admin" component={Admin} />
|
||||
</MainContent>
|
||||
</Switch>
|
||||
);
|
||||
|
@ -1,24 +1,172 @@
|
||||
import React, { useState, useContext } from 'react';
|
||||
import React, { useState, useContext, useEffect } from 'react';
|
||||
import TopNavbar from 'shared/components/TopNavbar';
|
||||
import styled from 'styled-components/macro';
|
||||
import DropdownMenu, { ProfileMenu } from 'shared/components/DropdownMenu';
|
||||
import ProjectSettings, { DeleteProject } from 'shared/components/ProjectSettings';
|
||||
import ProjectSettings, { DeleteConfirm, DELETE_INFO } from 'shared/components/ProjectSettings';
|
||||
import { useHistory } from 'react-router';
|
||||
import UserIDContext from 'App/context';
|
||||
import { useMeQuery, useDeleteProjectMutation, GetProjectsDocument } from 'shared/generated/graphql';
|
||||
import {
|
||||
useMeQuery,
|
||||
useDeleteProjectMutation,
|
||||
useGetProjectsQuery,
|
||||
GetProjectsDocument,
|
||||
} from 'shared/generated/graphql';
|
||||
import { usePopup, Popup } from 'shared/components/PopupMenu';
|
||||
import { History } from 'history';
|
||||
import produce from 'immer';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
type GlobalTopNavbarProps = {
|
||||
projectID: string | null;
|
||||
name: string | null;
|
||||
projectMembers?: null | Array<TaskUser>;
|
||||
onSaveProjectName?: (projectName: string) => void;
|
||||
const TeamContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
`;
|
||||
|
||||
const TeamTitle = styled.h3`
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
`;
|
||||
|
||||
const TeamProjects = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-top: 8px;
|
||||
margin-bottom: 4px;
|
||||
`;
|
||||
|
||||
const TeamProjectLink = styled(Link)`
|
||||
display: flex;
|
||||
font-weight: 700;
|
||||
height: 36px;
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
position: relative;
|
||||
text-decoration: none;
|
||||
user-select: none;
|
||||
`;
|
||||
|
||||
const TeamProjectBackground = styled.div<{ color: string }>`
|
||||
background-image: url(null);
|
||||
background-color: ${props => props.color};
|
||||
|
||||
background-size: cover;
|
||||
background-position: 50%;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 36px;
|
||||
opacity: 1;
|
||||
border-radius: 3px;
|
||||
&:before {
|
||||
background: rgba(${props => props.theme.colors.bg.secondary});
|
||||
bottom: 0;
|
||||
content: '';
|
||||
left: 0;
|
||||
opacity: 0.88;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
}
|
||||
`;
|
||||
|
||||
const TeamProjectAvatar = styled.div<{ color: string }>`
|
||||
background-image: url(null);
|
||||
background-color: ${props => props.color};
|
||||
|
||||
display: inline-block;
|
||||
flex: 0 0 auto;
|
||||
background-size: cover;
|
||||
border-radius: 3px 0 0 3px;
|
||||
height: 36px;
|
||||
width: 36px;
|
||||
position: relative;
|
||||
opacity: 0.7;
|
||||
`;
|
||||
|
||||
const TeamProjectContent = styled.div`
|
||||
display: flex;
|
||||
position: relative;
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
padding: 9px 0 9px 10px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
`;
|
||||
|
||||
const TeamProjectTitle = styled.div`
|
||||
font-weight: 700;
|
||||
display: block;
|
||||
padding-right: 12px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
`;
|
||||
|
||||
const TeamProjectContainer = styled.div`
|
||||
box-sizing: border-box;
|
||||
border-radius: 3px;
|
||||
position: relative;
|
||||
margin: 0 4px 4px 0;
|
||||
min-width: 0;
|
||||
&:hover ${TeamProjectTitle} {
|
||||
color: rgba(${props => props.theme.colors.text.secondary});
|
||||
}
|
||||
&:hover ${TeamProjectAvatar} {
|
||||
opacity: 1;
|
||||
}
|
||||
&:hover ${TeamProjectBackground}:before {
|
||||
opacity: 0.78;
|
||||
}
|
||||
`;
|
||||
|
||||
const colors = ['#e362e3', '#7a6ff0', '#37c5ab', '#aa62e3', '#e8384f'];
|
||||
|
||||
const ProjectFinder = () => {
|
||||
const { loading, data } = useGetProjectsQuery();
|
||||
if (loading) {
|
||||
return <span>loading</span>;
|
||||
}
|
||||
if (data) {
|
||||
const { projects, teams, organizations } = data;
|
||||
const projectTeams = teams.map(team => {
|
||||
return {
|
||||
id: team.id,
|
||||
name: team.name,
|
||||
projects: projects.filter(project => project.team.id === team.id),
|
||||
};
|
||||
});
|
||||
return (
|
||||
<>
|
||||
{projectTeams.map(team => (
|
||||
<TeamContainer>
|
||||
<TeamTitle>{team.name}</TeamTitle>
|
||||
<TeamProjects>
|
||||
{team.projects.map((project, idx) => (
|
||||
<TeamProjectContainer>
|
||||
<TeamProjectLink to={`/projects/${project.id}`}>
|
||||
<TeamProjectBackground color={colors[idx % 5]} />
|
||||
<TeamProjectAvatar color={colors[idx % 5]} />
|
||||
<TeamProjectContent>
|
||||
<TeamProjectTitle>{project.name}</TeamProjectTitle>
|
||||
</TeamProjectContent>
|
||||
</TeamProjectLink>
|
||||
</TeamProjectContainer>
|
||||
))}
|
||||
</TeamProjects>
|
||||
</TeamContainer>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
return <span>error</span>;
|
||||
};
|
||||
const GlobalTopNavbar: React.FC<GlobalTopNavbarProps> = ({ projectID, name, projectMembers, onSaveProjectName }) => {
|
||||
const { loading, data } = useMeQuery();
|
||||
const { showPopup, hidePopup, setTab } = usePopup();
|
||||
const history = useHistory();
|
||||
const { userID, setUserID } = useContext(UserIDContext);
|
||||
type ProjectPopupProps = {
|
||||
history: History<History.PoorMansUnknown>;
|
||||
name: string;
|
||||
projectID: string;
|
||||
};
|
||||
|
||||
export const ProjectPopup: React.FC<ProjectPopupProps> = ({ history, name, projectID }) => {
|
||||
const { hidePopup, setTab } = usePopup();
|
||||
const [deleteProject] = useDeleteProjectMutation({
|
||||
update: (client, deleteData) => {
|
||||
const cacheData: any = client.readQuery({
|
||||
@ -42,6 +190,62 @@ const GlobalTopNavbar: React.FC<GlobalTopNavbarProps> = ({ projectID, name, proj
|
||||
});
|
||||
},
|
||||
});
|
||||
return (
|
||||
<>
|
||||
<Popup title={null} tab={0}>
|
||||
<ProjectSettings
|
||||
onDeleteProject={() => {
|
||||
setTab(1);
|
||||
}}
|
||||
/>
|
||||
</Popup>
|
||||
<Popup title={`Delete the "${name}" project?`} tab={1}>
|
||||
<DeleteConfirm
|
||||
description={DELETE_INFO.DELETE_PROJECTS.description}
|
||||
deletedItems={DELETE_INFO.DELETE_PROJECTS.deletedItems}
|
||||
onConfirmDelete={() => {
|
||||
if (projectID) {
|
||||
deleteProject({ variables: { projectID } });
|
||||
hidePopup();
|
||||
history.push('/projects');
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Popup>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
type GlobalTopNavbarProps = {
|
||||
nameOnly?: boolean;
|
||||
projectID: string | null;
|
||||
name: string | null;
|
||||
initialTab?: number;
|
||||
popupContent?: JSX.Element;
|
||||
menuType?: Array<string>;
|
||||
projectMembers?: null | Array<TaskUser>;
|
||||
onSaveProjectName?: (projectName: string) => void;
|
||||
};
|
||||
|
||||
const GlobalTopNavbar: React.FC<GlobalTopNavbarProps> = ({
|
||||
initialTab,
|
||||
menuType,
|
||||
projectID,
|
||||
name,
|
||||
popupContent,
|
||||
projectMembers,
|
||||
onSaveProjectName,
|
||||
nameOnly,
|
||||
}) => {
|
||||
console.log(popupContent);
|
||||
const { loading, data } = useMeQuery();
|
||||
const { showPopup, hidePopup, setTab } = usePopup();
|
||||
const history = useHistory();
|
||||
const [currentTab, setCurrentTab] = useState(initialTab);
|
||||
useEffect(() => {
|
||||
setCurrentTab(initialTab);
|
||||
}, [initialTab]);
|
||||
const { userID, setUserID } = useContext(UserIDContext);
|
||||
const onLogout = () => {
|
||||
fetch('http://localhost:3333/auth/logout', {
|
||||
method: 'POST',
|
||||
@ -61,42 +265,26 @@ const GlobalTopNavbar: React.FC<GlobalTopNavbarProps> = ({ projectID, name, proj
|
||||
<Popup title={null} tab={0}>
|
||||
<ProfileMenu
|
||||
onLogout={onLogout}
|
||||
onAdminConsole={() => {
|
||||
history.push('/admin');
|
||||
hidePopup();
|
||||
}}
|
||||
onProfile={() => {
|
||||
history.push('/profile');
|
||||
hidePopup();
|
||||
}}
|
||||
/>
|
||||
</Popup>,
|
||||
185,
|
||||
195,
|
||||
);
|
||||
};
|
||||
|
||||
const onOpenSettings = ($target: React.RefObject<HTMLElement>) => {
|
||||
showPopup(
|
||||
$target,
|
||||
<>
|
||||
<Popup title={null} tab={0}>
|
||||
<ProjectSettings
|
||||
onDeleteProject={() => {
|
||||
setTab(1, 325);
|
||||
}}
|
||||
/>
|
||||
</Popup>
|
||||
<Popup title={`Delete the "${name}" project?`} tab={1}>
|
||||
<DeleteProject
|
||||
name={name ?? ''}
|
||||
onDeleteProject={() => {
|
||||
if (projectID) {
|
||||
deleteProject({ variables: { projectID } });
|
||||
hidePopup();
|
||||
history.push('/projects');
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Popup>
|
||||
</>,
|
||||
185,
|
||||
);
|
||||
console.log('maybe firing popup');
|
||||
if (popupContent) {
|
||||
console.log('showing popup');
|
||||
showPopup($target, popupContent, 185);
|
||||
}
|
||||
};
|
||||
|
||||
if (!userID) {
|
||||
@ -105,12 +293,25 @@ const GlobalTopNavbar: React.FC<GlobalTopNavbarProps> = ({ projectID, name, proj
|
||||
return (
|
||||
<>
|
||||
<TopNavbar
|
||||
projectName={name}
|
||||
name={name}
|
||||
menuType={menuType}
|
||||
onOpenProjectFinder={$target => {
|
||||
showPopup(
|
||||
$target,
|
||||
<Popup tab={0} title={null}>
|
||||
<ProjectFinder />
|
||||
</Popup>,
|
||||
);
|
||||
}}
|
||||
currentTab={currentTab}
|
||||
user={data ? data.me : null}
|
||||
onNotificationClick={() => {}}
|
||||
onDashboardClick={() => {
|
||||
history.push('/');
|
||||
}}
|
||||
projectMembers={projectMembers}
|
||||
onProfileClick={onProfileClick}
|
||||
onSaveProjectName={onSaveProjectName}
|
||||
onSaveName={onSaveProjectName}
|
||||
onOpenSettings={onOpenSettings}
|
||||
/>
|
||||
</>
|
||||
|
@ -41,20 +41,19 @@ const App = () => {
|
||||
<>
|
||||
<UserIDContext.Provider value={{ userID, setUserID }}>
|
||||
<ThemeProvider theme={theme}>
|
||||
<PopupProvider>
|
||||
<NormalizeStyles />
|
||||
<BaseStyles />
|
||||
<Router history={history}>
|
||||
<NormalizeStyles />
|
||||
<BaseStyles />
|
||||
<Router history={history}>
|
||||
<PopupProvider>
|
||||
{loading ? (
|
||||
<div>loading</div>
|
||||
) : (
|
||||
<>
|
||||
<Navbar />
|
||||
<Routes history={history} />
|
||||
</>
|
||||
)}
|
||||
</Router>
|
||||
</PopupProvider>
|
||||
</PopupProvider>
|
||||
</Router>
|
||||
</ThemeProvider>
|
||||
</UserIDContext.Provider>
|
||||
</>
|
||||
|
@ -5,6 +5,9 @@ import PopupMenu, { Popup, usePopup } from 'shared/components/PopupMenu';
|
||||
import MemberManager from 'shared/components/MemberManager';
|
||||
import { useRouteMatch, useHistory } from 'react-router';
|
||||
import {
|
||||
useDeleteTaskChecklistMutation,
|
||||
useUpdateTaskChecklistNameMutation,
|
||||
useCreateTaskChecklistMutation,
|
||||
useFindTaskQuery,
|
||||
useUpdateTaskDueDateMutation,
|
||||
useSetTaskCompleteMutation,
|
||||
@ -20,6 +23,82 @@ import UserIDContext from 'App/context';
|
||||
import MiniProfile from 'shared/components/MiniProfile';
|
||||
import DueDateManager from 'shared/components/DueDateManager';
|
||||
import produce from 'immer';
|
||||
import styled from 'styled-components';
|
||||
import Button from 'shared/components/Button';
|
||||
import Input from 'shared/components/Input';
|
||||
import { useForm } from 'react-hook-form';
|
||||
|
||||
const calculateChecklistBadge = (draftState: any) => {
|
||||
const total = draftState.checklists.reduce((prev: any, next: any) => {
|
||||
return (
|
||||
prev +
|
||||
next.items.reduce((innerPrev: any, _item: any) => {
|
||||
return innerPrev + 1;
|
||||
}, 0)
|
||||
);
|
||||
}, 0);
|
||||
const complete = draftState.checklists.reduce(
|
||||
(prev: any, next: any) =>
|
||||
prev +
|
||||
next.items.reduce((innerPrev: any, item: any) => {
|
||||
return innerPrev + (item.complete ? 1 : 0);
|
||||
}, 0),
|
||||
0,
|
||||
);
|
||||
return { total, complete };
|
||||
};
|
||||
|
||||
const DeleteChecklistButton = styled(Button)`
|
||||
width: 100%;
|
||||
padding: 6px 12px;
|
||||
margin-top: 8px;
|
||||
`;
|
||||
type CreateChecklistData = {
|
||||
name: string;
|
||||
};
|
||||
const CreateChecklistForm = styled.form`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
`;
|
||||
|
||||
const CreateChecklistButton = styled(Button)`
|
||||
margin-top: 8px;
|
||||
padding: 6px 12px;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const CreateChecklistInput = styled(Input)`
|
||||
margin-bottom: 8px;
|
||||
`;
|
||||
|
||||
const InputError = styled.span`
|
||||
color: rgba(${props => props.theme.colors.danger});
|
||||
font-size: 12px;
|
||||
`;
|
||||
type CreateChecklistPopupProps = {
|
||||
onCreateChecklist: (data: CreateChecklistData) => void;
|
||||
};
|
||||
const CreateChecklistPopup: React.FC<CreateChecklistPopupProps> = ({ onCreateChecklist }) => {
|
||||
const { register, handleSubmit, errors } = useForm<CreateChecklistData>();
|
||||
const createUser = (data: CreateChecklistData) => {
|
||||
onCreateChecklist(data);
|
||||
};
|
||||
console.log(errors);
|
||||
return (
|
||||
<CreateChecklistForm onSubmit={handleSubmit(createUser)}>
|
||||
<CreateChecklistInput
|
||||
floatingLabel
|
||||
width="100%"
|
||||
label="Name"
|
||||
id="name"
|
||||
name="name"
|
||||
variant="alternate"
|
||||
ref={register({ required: 'Checklist name is required' })}
|
||||
/>
|
||||
<CreateChecklistButton type="submit">Create</CreateChecklistButton>
|
||||
</CreateChecklistForm>
|
||||
);
|
||||
};
|
||||
|
||||
type DetailsProps = {
|
||||
taskID: string;
|
||||
@ -50,8 +129,93 @@ const Details: React.FC<DetailsProps> = ({
|
||||
const match = useRouteMatch();
|
||||
const [currentMemberTask, setCurrentMemberTask] = useState('');
|
||||
const [memberPopupData, setMemberPopupData] = useState(initialMemberPopupState);
|
||||
const [setTaskChecklistItemComplete] = useSetTaskChecklistItemCompleteMutation();
|
||||
const [setTaskChecklistItemComplete] = useSetTaskChecklistItemCompleteMutation({
|
||||
update: client => {
|
||||
const cacheData: any = client.readQuery({
|
||||
query: FindTaskDocument,
|
||||
variables: { taskID },
|
||||
});
|
||||
const newData = produce(cacheData.findTask, (draftState: any) => {
|
||||
const { complete, total } = calculateChecklistBadge(draftState);
|
||||
if (!draftState.badges) {
|
||||
draftState.badges = {
|
||||
checklist: {},
|
||||
};
|
||||
}
|
||||
draftState.badges.checklist = {
|
||||
__typename: 'ChecklistBadge',
|
||||
complete,
|
||||
total,
|
||||
};
|
||||
});
|
||||
client.writeQuery({
|
||||
query: FindTaskDocument,
|
||||
variables: { taskID },
|
||||
data: {
|
||||
findTask: newData,
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
const [deleteTaskChecklist] = useDeleteTaskChecklistMutation({
|
||||
update: (client, deleteData) => {
|
||||
const cacheData: any = client.readQuery({
|
||||
query: FindTaskDocument,
|
||||
variables: { taskID },
|
||||
});
|
||||
console.log(deleteData);
|
||||
|
||||
const newData = produce(cacheData.findTask, (draftState: any) => {
|
||||
draftState.checklists = cacheData.findTask.checklists.filter(
|
||||
(checklist: any) => checklist.id !== deleteData.data.deleteTaskChecklist.taskChecklist.id,
|
||||
);
|
||||
const { complete, total } = calculateChecklistBadge(draftState);
|
||||
if (!draftState.badges) {
|
||||
draftState.badges = {
|
||||
checklist: {},
|
||||
};
|
||||
}
|
||||
draftState.badges.checklist = {
|
||||
__typename: 'ChecklistBadge',
|
||||
complete,
|
||||
total,
|
||||
};
|
||||
if (complete === 0 && total === 0) {
|
||||
draftState.badges.checklist = null;
|
||||
}
|
||||
});
|
||||
client.writeQuery({
|
||||
query: FindTaskDocument,
|
||||
variables: { taskID },
|
||||
data: {
|
||||
findTask: newData,
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
const [updateTaskChecklistItemName] = useUpdateTaskChecklistItemNameMutation();
|
||||
const [createTaskChecklist] = useCreateTaskChecklistMutation({
|
||||
update: (client, createData) => {
|
||||
const cacheData: any = client.readQuery({
|
||||
query: FindTaskDocument,
|
||||
variables: { taskID },
|
||||
});
|
||||
console.log(createData);
|
||||
|
||||
const newData = {
|
||||
findTask: {
|
||||
...cacheData.findTask,
|
||||
checklists: [...cacheData.findTask.checklists, { ...createData.data.createTaskChecklist }],
|
||||
},
|
||||
};
|
||||
client.writeQuery({
|
||||
query: FindTaskDocument,
|
||||
variables: { taskID },
|
||||
data: newData,
|
||||
});
|
||||
},
|
||||
});
|
||||
const [updateTaskChecklistName] = useUpdateTaskChecklistNameMutation();
|
||||
const [deleteTaskChecklistItem] = useDeleteTaskChecklistItemMutation({
|
||||
update: (client, deleteData) => {
|
||||
const cacheData: any = client.readQuery({
|
||||
@ -69,6 +233,17 @@ const Details: React.FC<DetailsProps> = ({
|
||||
draftState.checklists[idx].items = cacheData.findTask.checklists[idx].items.filter(
|
||||
(item: any) => item.id !== deleteData.data.deleteTaskChecklistItem.taskChecklistItem.id,
|
||||
);
|
||||
const { complete, total } = calculateChecklistBadge(draftState);
|
||||
if (!draftState.badges) {
|
||||
draftState.badges = {
|
||||
checklist: {},
|
||||
};
|
||||
}
|
||||
draftState.badges.checklist = {
|
||||
__typename: 'ChecklistBadge',
|
||||
complete,
|
||||
total,
|
||||
};
|
||||
}
|
||||
});
|
||||
client.writeQuery({
|
||||
@ -97,8 +272,23 @@ const Details: React.FC<DetailsProps> = ({
|
||||
...cacheData.findTask.checklists[idx].items,
|
||||
{ ...newTaskItem.data.createTaskChecklistItem },
|
||||
];
|
||||
console.log(draftState.checklists.map((item: any) => item.items));
|
||||
const { complete, total } = calculateChecklistBadge(draftState);
|
||||
if (!draftState.badges) {
|
||||
draftState.badges = {
|
||||
checklist: {},
|
||||
};
|
||||
}
|
||||
draftState.badges.checklist = {
|
||||
__typename: 'ChecklistBadge',
|
||||
complete,
|
||||
total,
|
||||
};
|
||||
console.log(draftState.badges.checklist);
|
||||
}
|
||||
});
|
||||
|
||||
console.log(newData);
|
||||
client.writeQuery({
|
||||
query: FindTaskDocument,
|
||||
variables: { taskID },
|
||||
@ -156,11 +346,24 @@ const Details: React.FC<DetailsProps> = ({
|
||||
updateTaskChecklistItemName({ variables: { taskChecklistItemID: itemID, name: itemName } });
|
||||
}}
|
||||
onCloseModal={() => history.push(projectURL)}
|
||||
onChangeChecklistName={(checklistID, newName) => {
|
||||
updateTaskChecklistName({ variables: { taskChecklistID: checklistID, name: newName } });
|
||||
}}
|
||||
onDeleteItem={itemID => {
|
||||
deleteTaskChecklistItem({ variables: { taskChecklistItemID: itemID } });
|
||||
}}
|
||||
onToggleChecklistItem={(itemID, complete) => {
|
||||
setTaskChecklistItemComplete({ variables: { taskChecklistItemID: itemID, complete } });
|
||||
setTaskChecklistItemComplete({
|
||||
variables: { taskChecklistItemID: itemID, complete },
|
||||
optimisticResponse: {
|
||||
__typename: 'Mutation',
|
||||
setTaskChecklistItemComplete: {
|
||||
__typename: 'TaskChecklistItem',
|
||||
id: itemID,
|
||||
complete,
|
||||
},
|
||||
},
|
||||
});
|
||||
}}
|
||||
onAddItem={(taskChecklistID, name, position) => {
|
||||
createTaskChecklistItem({ variables: { taskChecklistID, name, position } });
|
||||
@ -202,6 +405,57 @@ const Details: React.FC<DetailsProps> = ({
|
||||
);
|
||||
}}
|
||||
onOpenAddLabelPopup={onOpenAddLabelPopup}
|
||||
onOpenAddChecklistPopup={(_task, $target) => {
|
||||
showPopup(
|
||||
$target,
|
||||
<Popup
|
||||
title={'Add checklist'}
|
||||
tab={0}
|
||||
onClose={() => {
|
||||
hidePopup();
|
||||
}}
|
||||
>
|
||||
<CreateChecklistPopup
|
||||
onCreateChecklist={checklistData => {
|
||||
let position = 65535;
|
||||
console.log(data.findTask.checklists);
|
||||
if (data.findTask.checklists) {
|
||||
const [lastChecklist] = data.findTask.checklists.slice(-1);
|
||||
console.log(`lastCheclist ${lastChecklist}`);
|
||||
if (lastChecklist) {
|
||||
position = lastChecklist.position * 2 + 1;
|
||||
}
|
||||
}
|
||||
createTaskChecklist({
|
||||
variables: {
|
||||
taskID: data.findTask.id,
|
||||
name: checklistData.name,
|
||||
position,
|
||||
},
|
||||
});
|
||||
hidePopup();
|
||||
}}
|
||||
/>
|
||||
</Popup>,
|
||||
);
|
||||
}}
|
||||
onDeleteChecklist={($target, checklistID) => {
|
||||
showPopup(
|
||||
$target,
|
||||
<Popup tab={0} title="Delete checklist?" onClose={() => hidePopup()}>
|
||||
<p>Deleting a checklist is permanent and there is no way to get it back.</p>
|
||||
<DeleteChecklistButton
|
||||
color="danger"
|
||||
onClick={() => {
|
||||
deleteTaskChecklist({ variables: { taskChecklistID: checklistID } });
|
||||
hidePopup();
|
||||
}}
|
||||
>
|
||||
Delete Checklist
|
||||
</DeleteChecklistButton>
|
||||
</Popup>,
|
||||
);
|
||||
}}
|
||||
onOpenDueDatePopop={(task, $targetRef) => {
|
||||
showPopup(
|
||||
$targetRef,
|
||||
|
@ -1,7 +1,9 @@
|
||||
import React, { useState, useRef, useContext, useEffect } from 'react';
|
||||
import GlobalTopNavbar from 'App/TopNavbar';
|
||||
import { MENU_TYPES } from 'shared/components/TopNavbar';
|
||||
import GlobalTopNavbar, { ProjectPopup } from 'App/TopNavbar';
|
||||
import styled from 'styled-components/macro';
|
||||
import { Bolt, ToggleOn, Tags } from 'shared/icons';
|
||||
import { DataProxy } from '@apollo/client';
|
||||
import { Bolt, ToggleOn, Tags, CheckCircle, Sort, Filter } from 'shared/icons';
|
||||
import { usePopup, Popup } from 'shared/components/PopupMenu';
|
||||
import { useParams, Route, useRouteMatch, useHistory, RouteComponentProps } from 'react-router-dom';
|
||||
import {
|
||||
@ -26,6 +28,7 @@ import {
|
||||
useCreateProjectLabelMutation,
|
||||
useUnassignTaskMutation,
|
||||
useUpdateTaskDueDateMutation,
|
||||
FindProjectQuery,
|
||||
} from 'shared/generated/graphql';
|
||||
|
||||
import TaskAssignee from 'shared/components/TaskAssignee';
|
||||
@ -42,11 +45,35 @@ import produce from 'immer';
|
||||
import MiniProfile from 'shared/components/MiniProfile';
|
||||
import Details from './Details';
|
||||
import { useApolloClient } from '@apollo/react-hooks';
|
||||
import { DocumentNode } from 'graphql';
|
||||
import UserIDContext from 'App/context';
|
||||
import DueDateManager from 'shared/components/DueDateManager';
|
||||
|
||||
type UpdateCacheFn<T> = (cache: T) => T;
|
||||
function updateApolloCache<T>(client: DataProxy, document: DocumentNode, update: UpdateCacheFn<T>, variables?: object) {
|
||||
let queryArgs: DataProxy.Query<any>;
|
||||
if (variables) {
|
||||
queryArgs = {
|
||||
query: document,
|
||||
variables,
|
||||
};
|
||||
} else {
|
||||
queryArgs = {
|
||||
query: document,
|
||||
};
|
||||
}
|
||||
const cache: T | null = client.readQuery(queryArgs);
|
||||
if (cache) {
|
||||
const newCache = update(cache);
|
||||
client.writeQuery({
|
||||
...queryArgs,
|
||||
data: newCache,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const getCacheData = (client: any, projectID: string) => {
|
||||
const cacheData: any = client.readQuery({
|
||||
const cacheData: FindProjectQuery = client.readQuery({
|
||||
query: FindProjectDocument,
|
||||
variables: {
|
||||
projectId: projectID,
|
||||
@ -220,7 +247,7 @@ const initialQuickCardEditorState: QuickCardEditorState = {
|
||||
const ProjectBar = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
justify-content: space-between;
|
||||
height: 40px;
|
||||
padding: 0 12px;
|
||||
`;
|
||||
@ -302,13 +329,17 @@ const Project = () => {
|
||||
const [createTaskGroup] = useCreateTaskGroupMutation({
|
||||
onCompleted: newTaskGroupData => {},
|
||||
update: (client, newTaskGroupData) => {
|
||||
const cacheData = getCacheData(client, projectID);
|
||||
const newData = {
|
||||
...cacheData.findProject,
|
||||
taskGroups: [...cacheData.findProject.taskGroups, { ...newTaskGroupData.data.createTaskGroup, tasks: [] }],
|
||||
};
|
||||
|
||||
writeCacheData(client, projectID, cacheData, newData);
|
||||
updateApolloCache<FindProjectQuery>(
|
||||
client,
|
||||
FindProjectDocument,
|
||||
cache => {
|
||||
console.log(cache);
|
||||
return produce(cache, draftCache => {
|
||||
draftCache.findProject.taskGroups.push({ ...newTaskGroupData.data.createTaskGroup, tasks: [] });
|
||||
});
|
||||
},
|
||||
{ projectId: projectID },
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@ -316,7 +347,7 @@ const Project = () => {
|
||||
onCompleted: newTaskData => {},
|
||||
update: (client, newTaskData) => {
|
||||
const cacheData = getCacheData(client, projectID);
|
||||
const newTaskGroups = produce(cacheData.findProject.taskGroups, (draftState: any) => {
|
||||
const newTaskGroups = produce(cacheData.findProject.taskGroups, draftState => {
|
||||
const targetIndex = draftState.findIndex(
|
||||
(taskGroup: any) => taskGroup.id === newTaskData.data.createTask.taskGroup.id,
|
||||
);
|
||||
@ -388,14 +419,18 @@ const Project = () => {
|
||||
createTask: {
|
||||
__typename: 'Task',
|
||||
id: '' + Math.round(Math.random() * -1000000),
|
||||
name: name,
|
||||
name,
|
||||
complete: false,
|
||||
taskGroup: {
|
||||
__typename: 'TaskGroup',
|
||||
id: taskGroup.id,
|
||||
name: taskGroup.name,
|
||||
position: taskGroup.position,
|
||||
},
|
||||
position: position,
|
||||
badges: {
|
||||
checklist: null,
|
||||
},
|
||||
position,
|
||||
dueDate: null,
|
||||
description: null,
|
||||
labels: [],
|
||||
@ -509,11 +544,28 @@ const Project = () => {
|
||||
onSaveProjectName={projectName => {
|
||||
updateProjectName({ variables: { projectID, name: projectName } });
|
||||
}}
|
||||
popupContent={<ProjectPopup history={history} name={data.findProject.name} projectID={projectID} />}
|
||||
menuType={MENU_TYPES.PROJECT_MENU}
|
||||
initialTab={0}
|
||||
projectMembers={data.findProject.members}
|
||||
projectID={projectID}
|
||||
name={data.findProject.name}
|
||||
/>
|
||||
<ProjectBar>
|
||||
<ProjectActions>
|
||||
<ProjectAction>
|
||||
<CheckCircle width={13} height={13} />
|
||||
<ProjectActionText>All Tasks</ProjectActionText>
|
||||
</ProjectAction>
|
||||
<ProjectAction>
|
||||
<Filter width={13} height={13} />
|
||||
<ProjectActionText>Filter</ProjectActionText>
|
||||
</ProjectAction>
|
||||
<ProjectAction>
|
||||
<Sort width={13} height={13} />
|
||||
<ProjectActionText>Sort</ProjectActionText>
|
||||
</ProjectAction>
|
||||
</ProjectActions>
|
||||
<ProjectActions>
|
||||
<ProjectAction
|
||||
ref={$labelsRef}
|
||||
|
@ -137,6 +137,7 @@ const ProjectTileName = styled.div<{ centered?: boolean }>`
|
||||
`;
|
||||
|
||||
const Wrapper = styled.div`
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: flex-start;
|
||||
@ -146,10 +147,24 @@ const Wrapper = styled.div`
|
||||
const ProjectSectionTitleWrapper = styled.div`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
height: 32px;
|
||||
margin-bottom: 24px;
|
||||
padding: 8px 0;
|
||||
position: relative;
|
||||
margin-top: 16px;
|
||||
`;
|
||||
|
||||
const SectionActions = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
`;
|
||||
|
||||
const SectionAction = styled(Button)`
|
||||
padding: 6px 12px;
|
||||
`;
|
||||
const SectionActionLink = styled(Link)`
|
||||
margin-right: 8px;
|
||||
`;
|
||||
|
||||
const ProjectSectionTitle = styled.h3`
|
||||
@ -171,13 +186,19 @@ const ProjectGrid = styled.div`
|
||||
`;
|
||||
const AddTeamButton = styled(Button)`
|
||||
padding: 6px 12px;
|
||||
float: right;
|
||||
position: absolute;
|
||||
top: 6px;
|
||||
right: 12px;
|
||||
`;
|
||||
type ShowNewProject = {
|
||||
open: boolean;
|
||||
initialTeamID: null | string;
|
||||
};
|
||||
|
||||
const ProjectLink = styled(Link)``;
|
||||
|
||||
const Projects = () => {
|
||||
const { showPopup } = usePopup();
|
||||
const { showPopup, hidePopup } = usePopup();
|
||||
const { loading, data } = useGetProjectsQuery();
|
||||
useEffect(() => {
|
||||
document.title = 'Citadel';
|
||||
@ -202,9 +223,24 @@ const Projects = () => {
|
||||
});
|
||||
},
|
||||
});
|
||||
const [showNewProject, setShowNewProject] = useState(false);
|
||||
|
||||
const [showNewProject, setShowNewProject] = useState<ShowNewProject>({ open: false, initialTeamID: null });
|
||||
const { userID, setUserID } = useContext(UserIDContext);
|
||||
const [createTeam] = useCreateTeamMutation();
|
||||
const [createTeam] = useCreateTeamMutation({
|
||||
update: (client, createData) => {
|
||||
const cacheData: any = client.readQuery({
|
||||
query: GetProjectsDocument,
|
||||
});
|
||||
const newData = {
|
||||
...cacheData,
|
||||
teams: [...cacheData.teams, { ...createData.data.createTeam }],
|
||||
};
|
||||
client.writeQuery({
|
||||
query: GetProjectsDocument,
|
||||
data: newData,
|
||||
});
|
||||
},
|
||||
});
|
||||
if (loading) {
|
||||
return (
|
||||
<>
|
||||
@ -234,11 +270,18 @@ const Projects = () => {
|
||||
onClick={$target => {
|
||||
showPopup(
|
||||
$target,
|
||||
<Popup title="Create team" tab={0}>
|
||||
<Popup
|
||||
title="Create team"
|
||||
tab={0}
|
||||
onClose={() => {
|
||||
hidePopup();
|
||||
}}
|
||||
>
|
||||
<CreateTeamForm
|
||||
onCreateTeam={teamName => {
|
||||
if (organizationID) {
|
||||
createTeam({ variables: { name: teamName, organizationID } });
|
||||
hidePopup();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
@ -253,6 +296,17 @@ const Projects = () => {
|
||||
<div key={team.id}>
|
||||
<ProjectSectionTitleWrapper>
|
||||
<ProjectSectionTitle>{team.name}</ProjectSectionTitle>
|
||||
<SectionActions>
|
||||
<SectionActionLink to={`/teams/${team.id}`}>
|
||||
<SectionAction variant="outline">Projects</SectionAction>
|
||||
</SectionActionLink>
|
||||
<SectionActionLink to="/">
|
||||
<SectionAction variant="outline">Members</SectionAction>
|
||||
</SectionActionLink>
|
||||
<SectionActionLink to="/">
|
||||
<SectionAction variant="outline">Settings</SectionAction>
|
||||
</SectionActionLink>
|
||||
</SectionActions>
|
||||
</ProjectSectionTitleWrapper>
|
||||
<ProjectList>
|
||||
{team.projects.map((project, idx) => (
|
||||
@ -268,7 +322,7 @@ const Projects = () => {
|
||||
<ProjectListItem>
|
||||
<ProjectAddTile
|
||||
onClick={() => {
|
||||
setShowNewProject(true);
|
||||
setShowNewProject({ open: true, initialTeamID: team.id });
|
||||
}}
|
||||
>
|
||||
<ProjectTileFade />
|
||||
@ -281,16 +335,17 @@ const Projects = () => {
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
{showNewProject && (
|
||||
{showNewProject.open && (
|
||||
<NewProject
|
||||
initialTeamID={showNewProject.initialTeamID}
|
||||
onCreateProject={(name, teamID) => {
|
||||
if (userID) {
|
||||
createProject({ variables: { teamID, name, userID } });
|
||||
setShowNewProject(false);
|
||||
setShowNewProject({ open: false, initialTeamID: null });
|
||||
}
|
||||
}}
|
||||
onClose={() => {
|
||||
setShowNewProject(false);
|
||||
setShowNewProject({ open: false, initialTeamID: null });
|
||||
}}
|
||||
teams={teams}
|
||||
/>
|
||||
|
227
web/src/Teams/index.tsx
Normal file
227
web/src/Teams/index.tsx
Normal file
@ -0,0 +1,227 @@
|
||||
import React, { useState, useContext, useEffect } from 'react';
|
||||
import styled from 'styled-components/macro';
|
||||
import { MENU_TYPES } from 'shared/components/TopNavbar';
|
||||
import GlobalTopNavbar from 'App/TopNavbar';
|
||||
import { useGetTeamQuery, useDeleteTeamMutation, GetProjectsDocument } from 'shared/generated/graphql';
|
||||
import { useParams, useHistory } from 'react-router';
|
||||
import { usePopup, Popup } from 'shared/components/PopupMenu';
|
||||
import { History } from 'history';
|
||||
import produce from 'immer';
|
||||
import { TeamSettings, DeleteConfirm, DELETE_INFO } from 'shared/components/ProjectSettings';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
const ProjectAddTile = styled.div`
|
||||
background-color: rgba(${props => props.theme.colors.bg.primary}, 0.4);
|
||||
background-size: cover;
|
||||
background-position: 50%;
|
||||
color: #fff;
|
||||
line-height: 20px;
|
||||
padding: 8px;
|
||||
position: relative;
|
||||
text-decoration: none;
|
||||
|
||||
border-radius: 3px;
|
||||
display: block;
|
||||
`;
|
||||
|
||||
const ProjectTile = styled(Link)<{ color: string }>`
|
||||
background-color: ${props => props.color};
|
||||
background-size: cover;
|
||||
background-position: 50%;
|
||||
color: #fff;
|
||||
line-height: 20px;
|
||||
padding: 8px;
|
||||
position: relative;
|
||||
text-decoration: none;
|
||||
|
||||
border-radius: 3px;
|
||||
display: block;
|
||||
`;
|
||||
|
||||
const ProjectTileFade = styled.div`
|
||||
background-color: rgba(0, 0, 0, 0.15);
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
`;
|
||||
|
||||
const ProjectListItem = styled.li`
|
||||
width: 23.5%;
|
||||
padding: 0;
|
||||
margin: 0 2% 2% 0;
|
||||
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover ${ProjectTileFade} {
|
||||
background-color: rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
`;
|
||||
|
||||
const ProjectList = styled.ul`
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
& ${ProjectListItem}:nth-of-type(4n) {
|
||||
margin-right: 0;
|
||||
}
|
||||
`;
|
||||
|
||||
const ProjectTileDetails = styled.div`
|
||||
display: flex;
|
||||
height: 80px;
|
||||
position: relative;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
`;
|
||||
|
||||
const ProjectAddTileDetails = styled.div`
|
||||
display: flex;
|
||||
height: 80px;
|
||||
position: relative;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
`;
|
||||
|
||||
const ProjectTileName = styled.div<{ centered?: boolean }>`
|
||||
flex: 0 0 auto;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
max-height: 40px;
|
||||
width: 100%;
|
||||
word-wrap: break-word;
|
||||
${props => props.centered && 'text-align: center;'}
|
||||
`;
|
||||
|
||||
const Wrapper = styled.div`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
`;
|
||||
|
||||
type TeamPopupProps = {
|
||||
history: History<History.PoorMansUnknown>;
|
||||
name: string;
|
||||
teamID: string;
|
||||
};
|
||||
|
||||
export const TeamPopup: React.FC<TeamPopupProps> = ({ history, name, teamID }) => {
|
||||
const { hidePopup, setTab } = usePopup();
|
||||
const [deleteTeam] = useDeleteTeamMutation({
|
||||
update: (client, deleteData) => {
|
||||
const cacheData: any = client.readQuery({
|
||||
query: GetProjectsDocument,
|
||||
});
|
||||
|
||||
console.log(cacheData);
|
||||
console.log(deleteData);
|
||||
|
||||
const newData = produce(cacheData, (draftState: any) => {
|
||||
draftState.teams = draftState.teams.filter((team: any) => team.id !== deleteData.data.deleteTeam.team.id);
|
||||
draftState.projects = draftState.projects.filter(
|
||||
(project: any) => project.team.id !== deleteData.data.deleteTeam.team.id,
|
||||
);
|
||||
});
|
||||
|
||||
client.writeQuery({
|
||||
query: GetProjectsDocument,
|
||||
data: {
|
||||
...newData,
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
return (
|
||||
<>
|
||||
<Popup title={null} tab={0}>
|
||||
<TeamSettings
|
||||
onDeleteTeam={() => {
|
||||
setTab(1, 340);
|
||||
}}
|
||||
/>
|
||||
</Popup>
|
||||
<Popup title={`Delete the "${name}" team?`} tab={1} onClose={() => hidePopup()}>
|
||||
<DeleteConfirm
|
||||
description={DELETE_INFO.DELETE_TEAMS.description}
|
||||
deletedItems={DELETE_INFO.DELETE_TEAMS.deletedItems}
|
||||
onConfirmDelete={() => {
|
||||
if (teamID) {
|
||||
deleteTeam({ variables: { teamID } });
|
||||
hidePopup();
|
||||
history.push('/projects');
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Popup>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const ProjectsContainer = styled.div`
|
||||
margin: 40px 16px 0;
|
||||
width: 100%;
|
||||
max-width: 825px;
|
||||
min-width: 288px;
|
||||
`;
|
||||
|
||||
type TeamsRouteProps = {
|
||||
teamID: string;
|
||||
};
|
||||
|
||||
const colors = ['#e362e3', '#7a6ff0', '#37c5ab', '#aa62e3', '#e8384f'];
|
||||
const Projects = () => {
|
||||
const { teamID } = useParams<TeamsRouteProps>();
|
||||
const history = useHistory();
|
||||
const { loading, data } = useGetTeamQuery({ variables: { teamID } });
|
||||
useEffect(() => {
|
||||
document.title = 'Citadel | Teams';
|
||||
}, []);
|
||||
if (loading) {
|
||||
return (
|
||||
<>
|
||||
<span>loading</span>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
if (data) {
|
||||
return (
|
||||
<>
|
||||
<GlobalTopNavbar
|
||||
menuType={MENU_TYPES.TEAM_MENU}
|
||||
initialTab={0}
|
||||
popupContent={<TeamPopup history={history} name={data.findTeam.name} teamID={teamID} />}
|
||||
onSaveProjectName={() => {}}
|
||||
projectID={null}
|
||||
name={data.findTeam.name}
|
||||
/>
|
||||
<Wrapper>
|
||||
<ProjectsContainer>
|
||||
<ProjectList>
|
||||
{data.projects.map((project, idx) => (
|
||||
<ProjectListItem key={project.id}>
|
||||
<ProjectTile color={colors[idx % 5]} to={`/projects/${project.id}`}>
|
||||
<ProjectTileFade />
|
||||
<ProjectTileDetails>
|
||||
<ProjectTileName>{project.name}</ProjectTileName>
|
||||
</ProjectTileDetails>
|
||||
</ProjectTile>
|
||||
</ProjectListItem>
|
||||
))}
|
||||
</ProjectList>
|
||||
</ProjectsContainer>
|
||||
</Wrapper>
|
||||
</>
|
||||
);
|
||||
}
|
||||
return <div>Error!</div>;
|
||||
};
|
||||
|
||||
export default Projects;
|
9
web/src/citadel.d.ts
vendored
9
web/src/citadel.d.ts
vendored
@ -17,6 +17,15 @@ type ContextMenuEvent = {
|
||||
taskGroupID: string;
|
||||
};
|
||||
|
||||
type User = {
|
||||
id: string;
|
||||
fullName: string;
|
||||
username: string;
|
||||
email: string;
|
||||
role: string;
|
||||
profileIcon: ProfileIcon;
|
||||
};
|
||||
|
||||
type TaskUser = {
|
||||
id: string;
|
||||
fullName: string;
|
||||
|
10
web/src/projects.d.ts
vendored
10
web/src/projects.d.ts
vendored
@ -47,10 +47,20 @@ type TaskChecklistItem = {
|
||||
dueDate?: null | string;
|
||||
};
|
||||
|
||||
type ChecklistBadge = {
|
||||
complete: number;
|
||||
total: number;
|
||||
};
|
||||
|
||||
type TaskBadges = {
|
||||
checklist?: ChecklistBadge | null;
|
||||
};
|
||||
|
||||
type Task = {
|
||||
id: string;
|
||||
taskGroup: InnerTaskGroup;
|
||||
name: string;
|
||||
badges?: TaskBadges;
|
||||
position: number;
|
||||
dueDate?: string;
|
||||
complete?: boolean;
|
||||
|
@ -1,7 +1,10 @@
|
||||
import React, { useRef } from 'react';
|
||||
import Admin from '.';
|
||||
import { theme } from 'App/ThemeStyles';
|
||||
import NormalizeStyles from 'App/NormalizeStyles';
|
||||
import BaseStyles from 'App/BaseStyles';
|
||||
import { ThemeProvider } from 'styled-components';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
|
||||
export default {
|
||||
component: Admin,
|
||||
@ -19,7 +22,27 @@ export const Default = () => {
|
||||
<>
|
||||
<NormalizeStyles />
|
||||
<BaseStyles />
|
||||
<Admin />
|
||||
<ThemeProvider theme={theme}>
|
||||
<Admin
|
||||
onInviteUser={action('invite user')}
|
||||
initialTab={1}
|
||||
users={[
|
||||
{
|
||||
id: '1',
|
||||
username: 'jordanthedev',
|
||||
email: 'jordan@jordanthedev.com',
|
||||
role: 'Admin',
|
||||
fullName: 'Jordan Knott',
|
||||
profileIcon: {
|
||||
bgColor: '#fff',
|
||||
initials: 'JK',
|
||||
url: null,
|
||||
},
|
||||
},
|
||||
]}
|
||||
onAddUser={action('add user')}
|
||||
/>
|
||||
</ThemeProvider>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@ -1,34 +1,28 @@
|
||||
import React, { useState, useRef } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { User, Plus } from 'shared/icons';
|
||||
import { User, Plus, Lock, Pencil, Trash } from 'shared/icons';
|
||||
import { AgGridReact } from 'ag-grid-react';
|
||||
|
||||
import 'ag-grid-community/dist/styles/ag-grid.css';
|
||||
import 'ag-grid-community/dist/styles/ag-theme-material.css';
|
||||
import Button from 'shared/components/Button';
|
||||
|
||||
const NewUserButton = styled.button`
|
||||
outline: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
line-height: 20px;
|
||||
padding: 0.75rem;
|
||||
background-color: transparent;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: rgba(115, 103, 240);
|
||||
font-size: 14px;
|
||||
|
||||
border-radius: 0.5rem;
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
border-color: transparent;
|
||||
border-image: initial;
|
||||
border-color: rgba(115, 103, 240);
|
||||
span {
|
||||
padding-left: 0.5rem;
|
||||
}
|
||||
const NewUserButton = styled(Button)`
|
||||
padding: 6px 12px;
|
||||
margin-right: 12px;
|
||||
`;
|
||||
|
||||
const InviteUserButton = styled(Button)`
|
||||
padding: 6px 12px;
|
||||
margin-right: 8px;
|
||||
`;
|
||||
|
||||
const MemberActions = styled.div`
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
`;
|
||||
|
||||
const GridTable = styled.div`
|
||||
height: 620px;
|
||||
`;
|
||||
@ -93,8 +87,29 @@ const Header = styled.div`
|
||||
min-height: 112px;
|
||||
`;
|
||||
|
||||
const ActionButtonsContainer = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
`;
|
||||
|
||||
const EditUserIcon = styled(Pencil)`
|
||||
margin-right: 8px;
|
||||
`;
|
||||
|
||||
const LockUserIcon = styled(Lock)`
|
||||
margin-right: 8px;
|
||||
`;
|
||||
|
||||
const DeleteUserIcon = styled(Trash)``;
|
||||
|
||||
const ActionButtons = () => {
|
||||
return <span>Hello!</span>;
|
||||
return (
|
||||
<>
|
||||
<EditUserIcon width={16} height={16} />
|
||||
<LockUserIcon width={16} height={16} />
|
||||
<DeleteUserIcon width={16} height={16} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
const data = {
|
||||
defaultColDef: {
|
||||
@ -103,16 +118,14 @@ const data = {
|
||||
},
|
||||
columnDefs: [
|
||||
{
|
||||
minWidth: 125,
|
||||
width: 125,
|
||||
minWidth: 55,
|
||||
width: 55,
|
||||
headerCheckboxSelection: true,
|
||||
checkboxSelection: true,
|
||||
headerName: 'ID',
|
||||
field: 'id',
|
||||
},
|
||||
{ minWidth: 210, headerName: 'Username', editable: true, field: 'username' },
|
||||
{ minWidth: 225, headerName: 'Email', field: 'email' },
|
||||
{ minWidth: 200, headerName: 'Name', editable: true, field: 'full_name' },
|
||||
{ minWidth: 200, headerName: 'Name', editable: true, field: 'fullName' },
|
||||
{ minWidth: 200, headerName: 'Role', editable: true, field: 'role' },
|
||||
{
|
||||
minWidth: 200,
|
||||
@ -123,14 +136,13 @@ const data = {
|
||||
frameworkComponents: {
|
||||
actionButtons: ActionButtons,
|
||||
},
|
||||
rowData: [
|
||||
{ id: '1', full_name: 'Jordan Knott', username: 'jordan', email: 'jordan@jordanthedev.com', role: 'Admin' },
|
||||
{ id: '2', full_name: 'Jordan Test', username: 'jordantest', email: 'jordan@jordanthedev.com', role: 'Admin' },
|
||||
{ id: '3', full_name: 'Jordan Other', username: 'alphatest1050', email: 'jordan@jordanthedev.com', role: 'Admin' },
|
||||
{ id: '5', full_name: 'Jordan French', username: 'other', email: 'jordan@jordanthedev.com', role: 'Admin' },
|
||||
],
|
||||
};
|
||||
const ListTable = () => {
|
||||
|
||||
type ListTableProps = {
|
||||
users: Array<User>;
|
||||
};
|
||||
|
||||
const ListTable: React.FC<ListTableProps> = ({ users }) => {
|
||||
return (
|
||||
<Root>
|
||||
<div className="ag-theme-material" style={{ height: '296px', width: '100%' }}>
|
||||
@ -138,7 +150,7 @@ const ListTable = () => {
|
||||
rowSelection="multiple"
|
||||
defaultColDef={data.defaultColDef}
|
||||
columnDefs={data.columnDefs}
|
||||
rowData={data.rowData}
|
||||
rowData={users}
|
||||
frameworkComponents={data.frameworkComponents}
|
||||
onFirstDataRendered={params => {
|
||||
params.api.sizeColumnsToFit();
|
||||
@ -146,7 +158,7 @@ const ListTable = () => {
|
||||
onGridSizeChanged={params => {
|
||||
params.api.sizeColumnsToFit();
|
||||
}}
|
||||
></AgGridReact>
|
||||
/>
|
||||
</div>
|
||||
</Root>
|
||||
);
|
||||
@ -184,6 +196,7 @@ const TabNavContent = styled.ul`
|
||||
|
||||
const TabNavItem = styled.li`
|
||||
padding: 0.35rem 0.3rem;
|
||||
height: 48px;
|
||||
display: block;
|
||||
position: relative;
|
||||
`;
|
||||
@ -282,9 +295,16 @@ const NavItem: React.FC<NavItemProps> = ({ active, name, tab, onClick }) => {
|
||||
);
|
||||
};
|
||||
|
||||
const Admin = () => {
|
||||
const [currentTop, setTop] = useState(0);
|
||||
const [currentTab, setTab] = useState(0);
|
||||
type AdminProps = {
|
||||
initialTab: number;
|
||||
onAddUser: ($target: React.RefObject<HTMLElement>) => void;
|
||||
onInviteUser: ($target: React.RefObject<HTMLElement>) => void;
|
||||
users: Array<User>;
|
||||
};
|
||||
|
||||
const Admin: React.FC<AdminProps> = ({ initialTab, onAddUser, onInviteUser, users }) => {
|
||||
const [currentTop, setTop] = useState(initialTab * 48);
|
||||
const [currentTab, setTab] = useState(initialTab);
|
||||
const $tabNav = useRef<HTMLDivElement>(null);
|
||||
return (
|
||||
<Container>
|
||||
@ -309,11 +329,17 @@ const Admin = () => {
|
||||
</TabNav>
|
||||
<TabContentWrapper>
|
||||
<TabContent>
|
||||
<NewUserButton>
|
||||
<Plus color="rgba(115, 103, 240)" size={10} />
|
||||
<span>Add New</span>
|
||||
</NewUserButton>
|
||||
<ListTable />
|
||||
<MemberActions>
|
||||
<NewUserButton variant="outline" onClick={onAddUser}>
|
||||
<Plus color="rgba(115, 103, 240)" size={10} />
|
||||
<span style={{ paddingLeft: '5px' }}>Create member</span>
|
||||
</NewUserButton>
|
||||
<InviteUserButton variant="outline" onClick={onInviteUser}>
|
||||
<Plus color="rgba(115, 103, 240)" size={10} />
|
||||
<span style={{ paddingLeft: '5px' }}>Invite member</span>
|
||||
</InviteUserButton>
|
||||
</MemberActions>
|
||||
<ListTable users={users} />
|
||||
</TabContent>
|
||||
</TabContentWrapper>
|
||||
</Container>
|
||||
|
@ -42,7 +42,7 @@ type Props = {
|
||||
onClick?: (e: React.MouseEvent<HTMLDivElement>) => void;
|
||||
description?: null | string;
|
||||
dueDate?: DueDate;
|
||||
checklists?: Checklist;
|
||||
checklists?: Checklist | null;
|
||||
labels?: Array<ProjectLabel>;
|
||||
watched?: boolean;
|
||||
wrapperProps?: any;
|
||||
|
@ -501,7 +501,7 @@ const ChecklistTitleEditor = React.forwardRef(
|
||||
);
|
||||
type ChecklistProps = {
|
||||
checklistID: string;
|
||||
onDeleteChecklist: (checklistID: string) => void;
|
||||
onDeleteChecklist: ($target: React.RefObject<HTMLElement>, checklistID: string) => void;
|
||||
name: string;
|
||||
onChangeName: (item: string) => void;
|
||||
onToggleItem: (taskID: string, complete: boolean) => void;
|
||||
@ -554,8 +554,8 @@ const Checklist: React.FC<ChecklistProps> = ({
|
||||
<WindowTitleText onClick={() => setEditting(true)}>{name}</WindowTitleText>
|
||||
<WindowOptions>
|
||||
<DeleteButton
|
||||
onClick={() => {
|
||||
onDeleteChecklist(checklistID);
|
||||
onClick={$target => {
|
||||
onDeleteChecklist($target, checklistID);
|
||||
}}
|
||||
color="danger"
|
||||
variant="outline"
|
||||
|
@ -51,6 +51,7 @@ export const Default = () => {
|
||||
</Container>
|
||||
{menu.isOpen && (
|
||||
<DropdownMenu
|
||||
onAdminConsole={action('admin')}
|
||||
onCloseDropdown={() => {
|
||||
setMenu({ top: 0, left: 0, isOpen: false });
|
||||
}}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React, { useRef } from 'react';
|
||||
import useOnOutsideClick from 'shared/hooks/onOutsideClick';
|
||||
import { Exit, User } from 'shared/icons';
|
||||
import { Exit, User, Cog } from 'shared/icons';
|
||||
import { Separator, Container, WrapperDiamond, Wrapper, ActionsList, ActionItem, ActionTitle } from './Styles';
|
||||
|
||||
type DropdownMenuProps = {
|
||||
@ -8,15 +8,16 @@ type DropdownMenuProps = {
|
||||
top: number;
|
||||
onLogout: () => void;
|
||||
onCloseDropdown: () => void;
|
||||
onAdminConsole: () => void;
|
||||
};
|
||||
|
||||
const DropdownMenu: React.FC<DropdownMenuProps> = ({ left, top, onLogout, onCloseDropdown }) => {
|
||||
const DropdownMenu: React.FC<DropdownMenuProps> = ({ left, top, onLogout, onCloseDropdown, onAdminConsole }) => {
|
||||
const $containerRef = useRef<HTMLDivElement>(null);
|
||||
useOnOutsideClick($containerRef, true, onCloseDropdown, null);
|
||||
return (
|
||||
<Container ref={$containerRef} left={left} top={top}>
|
||||
<Wrapper>
|
||||
<ActionItem>
|
||||
<ActionItem onClick={onAdminConsole}>
|
||||
<User size={16} color="#c2c6dc" />
|
||||
<ActionTitle>Profile</ActionTitle>
|
||||
</ActionItem>
|
||||
@ -36,16 +37,21 @@ const DropdownMenu: React.FC<DropdownMenuProps> = ({ left, top, onLogout, onClos
|
||||
type ProfileMenuProps = {
|
||||
onProfile: () => void;
|
||||
onLogout: () => void;
|
||||
onAdminConsole: () => void;
|
||||
};
|
||||
|
||||
const ProfileMenu: React.FC<ProfileMenuProps> = ({ onProfile, onLogout }) => {
|
||||
const ProfileMenu: React.FC<ProfileMenuProps> = ({ onAdminConsole, onProfile, onLogout }) => {
|
||||
return (
|
||||
<>
|
||||
<ActionItem onClick={onAdminConsole}>
|
||||
<Cog size={16} color="#c2c6dc" />
|
||||
<ActionTitle>Admin Console</ActionTitle>
|
||||
</ActionItem>
|
||||
<Separator />
|
||||
<ActionItem onClick={onProfile}>
|
||||
<User size={16} color="#c2c6dc" />
|
||||
<ActionTitle>Profile</ActionTitle>
|
||||
</ActionItem>
|
||||
<Separator />
|
||||
<ActionsList>
|
||||
<ActionItem onClick={onLogout}>
|
||||
<Exit size={16} color="#c2c6dc" />
|
||||
|
@ -80,8 +80,10 @@ type InputProps = {
|
||||
variant?: 'normal' | 'alternate';
|
||||
label?: string;
|
||||
width?: string;
|
||||
floatingLabel?: boolean;
|
||||
placeholder?: string;
|
||||
icon?: JSX.Element;
|
||||
autocomplete?: boolean;
|
||||
id?: string;
|
||||
name?: string;
|
||||
className?: string;
|
||||
@ -95,6 +97,7 @@ const Input = React.forwardRef(
|
||||
{
|
||||
width = 'auto',
|
||||
variant = 'normal',
|
||||
autocomplete,
|
||||
label,
|
||||
placeholder,
|
||||
icon,
|
||||
@ -102,6 +105,7 @@ const Input = React.forwardRef(
|
||||
onChange,
|
||||
className,
|
||||
onClick,
|
||||
floatingLabel,
|
||||
value: initialValue,
|
||||
id,
|
||||
}: InputProps,
|
||||
@ -124,12 +128,13 @@ const Input = React.forwardRef(
|
||||
return (
|
||||
<InputWrapper className={className} width={width}>
|
||||
<InputInput
|
||||
hasValue={value !== ''}
|
||||
hasValue={floatingLabel || value !== ''}
|
||||
ref={$ref}
|
||||
id={id}
|
||||
name={name}
|
||||
onClick={onClick}
|
||||
onChange={handleChange}
|
||||
autoComplete={autocomplete ? 'on' : 'off'}
|
||||
value={value}
|
||||
hasIcon={typeof icon !== 'undefined'}
|
||||
width={width}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const Container = styled.div`
|
||||
flex: 1;
|
||||
user-select: none;
|
||||
white-space: nowrap;
|
||||
margin-bottom: 8px;
|
||||
|
@ -181,6 +181,7 @@ const SimpleLists: React.FC<SimpleProps> = ({
|
||||
onClick={() => {
|
||||
onTaskClick(task);
|
||||
}}
|
||||
checklists={task.badges && task.badges.checklist}
|
||||
onCardMemberClick={onCardMemberClick}
|
||||
onContextMenu={onQuickEditorOpen}
|
||||
/>
|
||||
|
@ -38,7 +38,7 @@ const Login = ({ onSubmit }: LoginProps) => {
|
||||
<LoginFormWrapper>
|
||||
<LoginFormContainer>
|
||||
<LogoWrapper>
|
||||
<Citadel size={42} />
|
||||
<Citadel width={42} height={42} />
|
||||
<LogoTitle>Citadel</LogoTitle>
|
||||
</LogoWrapper>
|
||||
<Title>Login</Title>
|
||||
@ -66,7 +66,7 @@ const Login = ({ onSubmit }: LoginProps) => {
|
||||
ref={register({ required: 'Password is required' })}
|
||||
/>
|
||||
<FormIcon>
|
||||
<Lock color="#c2c6dc" size={20} />
|
||||
<Lock width={20} height={20} />
|
||||
</FormIcon>
|
||||
</FormLabel>
|
||||
{errors.password && <FormError>{errors.password.message}</FormError>}
|
||||
|
@ -28,10 +28,10 @@ export const Default = () => {
|
||||
<PrimaryLogo />
|
||||
<ButtonContainer>
|
||||
<ActionButton name="Home">
|
||||
<Home size={28} color="#c2c6dc" />
|
||||
<Home width={28} height={28} />
|
||||
</ActionButton>
|
||||
<ActionButton name="Home">
|
||||
<Home size={28} color="#c2c6dc" />
|
||||
<Home width={28} height={28} />
|
||||
</ActionButton>
|
||||
</ButtonContainer>
|
||||
</Navbar>
|
||||
|
@ -35,7 +35,7 @@ export const ButtonContainer: React.FC = ({ children }) => (
|
||||
export const PrimaryLogo = () => {
|
||||
return (
|
||||
<LogoWrapper>
|
||||
<Citadel size={42} />
|
||||
<Citadel width={42} height={42} />
|
||||
<LogoTitle>Citadel</LogoTitle>
|
||||
</LogoWrapper>
|
||||
);
|
||||
|
@ -23,6 +23,7 @@ export const Default = () => {
|
||||
<NormalizeStyles />
|
||||
<BaseStyles />
|
||||
<NewProject
|
||||
initialTeamID={null}
|
||||
onCreateProject={action('create project')}
|
||||
teams={[{ name: 'General', id: 'general', createdAt: '' }]}
|
||||
onClose={() => {}}
|
||||
|
@ -207,14 +207,15 @@ const CreateButton = styled.button`
|
||||
}
|
||||
`;
|
||||
type NewProjectProps = {
|
||||
initialTeamID: string | null;
|
||||
teams: Array<Team>;
|
||||
onClose: () => void;
|
||||
onCreateProject: (projectName: string, teamID: string) => void;
|
||||
};
|
||||
|
||||
const NewProject: React.FC<NewProjectProps> = ({ teams, onClose, onCreateProject }) => {
|
||||
const NewProject: React.FC<NewProjectProps> = ({ initialTeamID, teams, onClose, onCreateProject }) => {
|
||||
const [projectName, setProjectName] = useState('');
|
||||
const [team, setTeam] = useState<null | string>(null);
|
||||
const [team, setTeam] = useState<null | string>(initialTeamID);
|
||||
const options = teams.map(t => ({ label: t.name, value: t.id }));
|
||||
return (
|
||||
<Overlay>
|
||||
|
@ -58,7 +58,7 @@ const LabelManager: React.FC<Props> = ({ labels, taskLabels, onLabelToggle, onLa
|
||||
onLabelEdit(label.id);
|
||||
}}
|
||||
>
|
||||
<Pencil color="#c2c6dc" />
|
||||
<Pencil width={16} height={16} />
|
||||
</LabelIcon>
|
||||
<CardLabel
|
||||
key={label.id}
|
||||
|
@ -42,7 +42,6 @@ export const HeaderTitle = styled.span`
|
||||
box-sizing: border-box;
|
||||
color: #c2c6dc;
|
||||
display: block;
|
||||
line-height: 40px;
|
||||
border-bottom: 1px solid #414561;
|
||||
margin: 0 12px;
|
||||
overflow: hidden;
|
||||
@ -51,6 +50,13 @@ export const HeaderTitle = styled.span`
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
z-index: 1;
|
||||
|
||||
height: 40px;
|
||||
line-height: 18px;
|
||||
font-size: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
`;
|
||||
|
||||
export const Content = styled.div`
|
||||
@ -138,7 +144,7 @@ export const CardLabel = styled.span<{ active: boolean; color: string }>`
|
||||
`;
|
||||
|
||||
export const CloseButton = styled.div`
|
||||
padding: 10px 12px 10px 8px;
|
||||
padding: 18px 18px 14px 12px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
@ -312,7 +318,7 @@ export const CreateLabelButton = styled.button`
|
||||
`;
|
||||
|
||||
export const PreviousButton = styled.div`
|
||||
padding: 10px 12px 10px 8px;
|
||||
padding: 18px 18px 14px 12px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
|
@ -2,7 +2,6 @@ import React, { useRef } from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const Container = styled.div<{ size: number | string; bgColor: string | null; backgroundURL: string | null }>`
|
||||
margin-left: 10px;
|
||||
width: ${props => props.size}px;
|
||||
height: ${props => props.size}px;
|
||||
border-radius: 9999px;
|
||||
|
@ -52,6 +52,21 @@ const ProjectSettings: React.FC<Props> = ({ onDeleteProject }) => {
|
||||
);
|
||||
};
|
||||
|
||||
type TeamSettingsProps = {
|
||||
onDeleteTeam: () => void;
|
||||
};
|
||||
export const TeamSettings: React.FC<TeamSettingsProps> = ({ onDeleteTeam }) => {
|
||||
return (
|
||||
<>
|
||||
<ListActionsWrapper>
|
||||
<ListActionItemWrapper onClick={() => onDeleteTeam()}>
|
||||
<ListActionItem>Delete Team</ListActionItem>
|
||||
</ListActionItemWrapper>
|
||||
</ListActionsWrapper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const ConfirmWrapper = styled.div``;
|
||||
|
||||
const ConfirmSubTitle = styled.h3`
|
||||
@ -76,25 +91,40 @@ const ConfirmDeleteButton = styled(Button)`
|
||||
padding: 6px 12px;
|
||||
`;
|
||||
|
||||
type DeleteProjectProps = {
|
||||
name: string;
|
||||
onDeleteProject: () => void;
|
||||
type DeleteConfirmProps = {
|
||||
description: string;
|
||||
deletedItems: Array<string>;
|
||||
onConfirmDelete: () => void;
|
||||
};
|
||||
const DeleteProject: React.FC<DeleteProjectProps> = ({ name, onDeleteProject }) => {
|
||||
|
||||
export const DELETE_INFO = {
|
||||
DELETE_PROJECTS: {
|
||||
description: 'Deleting the project will also delete the following:',
|
||||
deletedItems: ['Task groups and tasks'],
|
||||
},
|
||||
DELETE_TEAMS: {
|
||||
description: 'Deleting the team will also delete the following:',
|
||||
deletedItems: ['Projects under the team', 'All task groups & tasks associated with the team projects'],
|
||||
},
|
||||
};
|
||||
|
||||
const DeleteConfirm: React.FC<DeleteConfirmProps> = ({ description, deletedItems, onConfirmDelete }) => {
|
||||
return (
|
||||
<ConfirmWrapper>
|
||||
<ConfirmDescription>
|
||||
Deleting the project will also delete the following:
|
||||
{description}
|
||||
<DeleteList>
|
||||
<DeleteListItem>Task groups and tasks</DeleteListItem>
|
||||
{deletedItems.map(item => (
|
||||
<DeleteListItem>{item}</DeleteListItem>
|
||||
))}
|
||||
</DeleteList>
|
||||
</ConfirmDescription>
|
||||
<ConfirmDeleteButton onClick={() => onDeleteProject()} color="danger">
|
||||
<ConfirmDeleteButton onClick={() => onConfirmDelete()} color="danger">
|
||||
Delete
|
||||
</ConfirmDeleteButton>
|
||||
</ConfirmWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export { DeleteProject };
|
||||
export { DeleteConfirm };
|
||||
export default ProjectSettings;
|
||||
|
@ -71,6 +71,9 @@ export const Default = () => {
|
||||
onToggleTaskComplete={action('toggle task complete')}
|
||||
onToggleChecklistItem={action('toggle checklist item')}
|
||||
onOpenAddLabelPopup={action('open add label popup')}
|
||||
onChangeChecklistName={action('change checklist name')}
|
||||
onDeleteChecklist={action('delete checklist')}
|
||||
onOpenAddChecklistPopup={action(' open checklist')}
|
||||
onOpenDueDatePopop={action('open due date popup')}
|
||||
/>
|
||||
);
|
||||
|
@ -140,13 +140,19 @@ type TaskDetailsProps = {
|
||||
onOpenAddMemberPopup: (task: Task, $targetRef: React.RefObject<HTMLElement>) => void;
|
||||
onOpenAddLabelPopup: (task: Task, $targetRef: React.RefObject<HTMLElement>) => void;
|
||||
onOpenDueDatePopop: (task: Task, $targetRef: React.RefObject<HTMLElement>) => void;
|
||||
onOpenAddChecklistPopup: (task: Task, $targetRef: React.RefObject<HTMLElement>) => void;
|
||||
onMemberProfile: ($targetRef: React.RefObject<HTMLElement>, memberID: string) => void;
|
||||
onChangeChecklistName: (checklistID: string, name: string) => void;
|
||||
onDeleteChecklist: ($target: React.RefObject<HTMLElement>, checklistID: string) => void;
|
||||
onCloseModal: () => void;
|
||||
};
|
||||
|
||||
const TaskDetails: React.FC<TaskDetailsProps> = ({
|
||||
task,
|
||||
onDeleteChecklist,
|
||||
onTaskNameChange,
|
||||
onOpenAddChecklistPopup,
|
||||
onChangeChecklistName,
|
||||
onToggleTaskComplete,
|
||||
onTaskDescriptionChange,
|
||||
onChangeItemName,
|
||||
@ -183,6 +189,9 @@ const TaskDetails: React.FC<TaskDetailsProps> = ({
|
||||
const onAddMember = ($target: React.RefObject<HTMLElement>) => {
|
||||
onOpenAddMemberPopup(task, $target);
|
||||
};
|
||||
const onAddChecklist = ($target: React.RefObject<HTMLElement>) => {
|
||||
onOpenAddChecklistPopup(task, $target)
|
||||
}
|
||||
const $dueDateLabel = useRef<HTMLDivElement>(null);
|
||||
const $addLabelRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
@ -290,16 +299,16 @@ const TaskDetails: React.FC<TaskDetailsProps> = ({
|
||||
name={checklist.name}
|
||||
checklistID={checklist.id}
|
||||
items={checklist.items}
|
||||
onDeleteChecklist={() => {}}
|
||||
onChangeName={() => {}}
|
||||
onDeleteChecklist={onDeleteChecklist}
|
||||
onChangeName={newName => onChangeChecklistName(checklist.id, newName)}
|
||||
onToggleItem={onToggleChecklistItem}
|
||||
onDeleteItem={onDeleteItem}
|
||||
onAddItem={n => {
|
||||
if (task.checklists) {
|
||||
let position = 1;
|
||||
const lastChecklist = task.checklists.sort((a, b) => a.position - b.position)[-1];
|
||||
if (lastChecklist) {
|
||||
position = lastChecklist.position * 2 + 1;
|
||||
let position = 65535;
|
||||
const [lastItem] = checklist.items.sort((a, b) => a.position - b.position).slice(-1);
|
||||
if (lastItem) {
|
||||
position = lastItem.position * 2 + 1;
|
||||
}
|
||||
onAddItem(checklist.id, n, position);
|
||||
}
|
||||
@ -317,7 +326,7 @@ const TaskDetails: React.FC<TaskDetailsProps> = ({
|
||||
</ActionButton>
|
||||
<ActionButton onClick={$target => onAddMember($target)}>Members</ActionButton>
|
||||
<ActionButton onClick={$target => onAddLabel($target)}>Labels</ActionButton>
|
||||
<ActionButton>Checklist</ActionButton>
|
||||
<ActionButton onClick={$target => onAddChecklist($target)}>Checklist</ActionButton>
|
||||
<ActionButton onClick={$target => onOpenDueDatePopop(task, $target)}>Due Date</ActionButton>
|
||||
<ActionButton>Attachment</ActionButton>
|
||||
<ActionButton>Cover</ActionButton>
|
||||
|
@ -2,6 +2,8 @@ import styled, { css } from 'styled-components';
|
||||
import TextareaAutosize from 'react-autosize-textarea';
|
||||
import { mixin } from 'shared/utils/styles';
|
||||
import Button from 'shared/components/Button';
|
||||
import { Citadel } from 'shared/icons';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
export const NavbarWrapper = styled.div`
|
||||
width: 100%;
|
||||
@ -9,7 +11,6 @@ export const NavbarWrapper = styled.div`
|
||||
|
||||
export const ProjectMembers = styled.div`
|
||||
display: flex;
|
||||
padding-right: 18px;
|
||||
align-items: center;
|
||||
`;
|
||||
export const NavbarHeader = styled.header`
|
||||
@ -34,9 +35,9 @@ export const BreadcrumpSeparator = styled.span`
|
||||
`;
|
||||
|
||||
export const ProjectActions = styled.div`
|
||||
flex: 1;
|
||||
align-items: flex-start;
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
min-width: 1px;
|
||||
`;
|
||||
@ -56,10 +57,11 @@ export const ProfileNameWrapper = styled.div`
|
||||
line-height: 1.25;
|
||||
`;
|
||||
|
||||
export const NotificationContainer = styled.div`
|
||||
export const IconContainer = styled.div`
|
||||
margin-right: 20px;
|
||||
cursor: pointer;
|
||||
`;
|
||||
|
||||
export const ProfileNamePrimary = styled.div`
|
||||
color: #c2c6dc;
|
||||
font-weight: 600;
|
||||
@ -70,7 +72,6 @@ export const ProfileNameSecondary = styled.small`
|
||||
`;
|
||||
|
||||
export const ProfileIcon = styled.div<{ bgColor: string | null; backgroundURL: string | null }>`
|
||||
margin-left: 10px;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 9999px;
|
||||
@ -84,9 +85,9 @@ export const ProfileIcon = styled.div<{ bgColor: string | null; backgroundURL: s
|
||||
background-size: contain;
|
||||
`;
|
||||
|
||||
export const ProjectMeta = styled.div`
|
||||
export const ProjectMeta = styled.div<{ nameOnly?: boolean }>`
|
||||
display: flex;
|
||||
padding-top: 9px;
|
||||
${props => !props.nameOnly && 'padding-top: 9px;'}
|
||||
margin-left: -14px;
|
||||
align-items: center;
|
||||
max-width: 100%;
|
||||
@ -188,7 +189,7 @@ export const ProjectSwitcher = styled.button`
|
||||
|
||||
export const Separator = styled.div`
|
||||
color: #c2c6dc;
|
||||
font-size: 16px;
|
||||
font-size: 20px;
|
||||
padding-left: 4px;
|
||||
padding-right: 4px;
|
||||
`;
|
||||
@ -214,3 +215,36 @@ export const InviteButton = styled(Button)`
|
||||
margin: 0 0 0 8px;
|
||||
padding: 6px 12px;
|
||||
`;
|
||||
|
||||
export const ProjectFinder = styled(Button)`
|
||||
margin-right: 20px;
|
||||
padding: 6px 12px;
|
||||
`;
|
||||
|
||||
export const NavSeparator = styled.div`
|
||||
width: 1px;
|
||||
background: rgba(${props => props.theme.colors.border});
|
||||
height: 34px;
|
||||
margin: 0 20px;
|
||||
`;
|
||||
|
||||
export const LogoContainer = styled(Link)`
|
||||
display: block;
|
||||
left: 50%;
|
||||
position: absolute;
|
||||
transform: translateX(-50%);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
`;
|
||||
|
||||
export const CitadelTitle = styled.h2`
|
||||
margin-left: 5px;
|
||||
color: rgba(${props => props.theme.colors.text.primary});
|
||||
font-size: 20px;
|
||||
`;
|
||||
|
||||
export const CitadelLogo = styled(Citadel)`
|
||||
fill: rgba(${props => props.theme.colors.text.primary});
|
||||
stroke: rgba(${props => props.theme.colors.text.primary});
|
||||
`;
|
||||
|
@ -26,7 +26,8 @@ export const Default = () => {
|
||||
<NormalizeStyles />
|
||||
<BaseStyles />
|
||||
<TopNavbar
|
||||
projectName="Projects"
|
||||
onOpenProjectFinder={action('finder')}
|
||||
name="Projects"
|
||||
user={{
|
||||
id: '1',
|
||||
fullName: 'Jordan Knott',
|
||||
@ -38,6 +39,7 @@ export const Default = () => {
|
||||
}}
|
||||
onNotificationClick={action('notifications click')}
|
||||
onOpenSettings={action('open settings')}
|
||||
onDashboardClick={action('open dashboard')}
|
||||
onProfileClick={action('profile click')}
|
||||
/>
|
||||
</>
|
||||
|
@ -1,22 +1,27 @@
|
||||
import React, { useRef, useState, useEffect } from 'react';
|
||||
import { Star, Ellipsis, Bell, Cog, AngleDown } from 'shared/icons';
|
||||
import { Home, Star, Bell, AngleDown, BarChart, CheckCircle } from 'shared/icons';
|
||||
import styled from 'styled-components';
|
||||
import ProfileIcon from 'shared/components/ProfileIcon';
|
||||
import TaskAssignee from 'shared/components/TaskAssignee';
|
||||
import { usePopup, Popup } from 'shared/components/PopupMenu';
|
||||
import MiniProfile from 'shared/components/MiniProfile';
|
||||
import {
|
||||
NotificationContainer,
|
||||
CitadelLogo,
|
||||
CitadelTitle,
|
||||
ProjectFinder,
|
||||
LogoContainer,
|
||||
NavSeparator,
|
||||
IconContainer,
|
||||
ProjectNameTextarea,
|
||||
InviteButton,
|
||||
GlobalActions,
|
||||
ProjectActions,
|
||||
ProjectSwitcher,
|
||||
Separator,
|
||||
ProjectMeta,
|
||||
ProjectName,
|
||||
ProjectTabs,
|
||||
ProjectTab,
|
||||
NavbarWrapper,
|
||||
NavbarHeader,
|
||||
Breadcrumbs,
|
||||
BreadcrumpSeparator,
|
||||
ProjectSettingsButton,
|
||||
ProfileContainer,
|
||||
ProfileNameWrapper,
|
||||
@ -24,18 +29,20 @@ import {
|
||||
ProfileNameSecondary,
|
||||
ProjectMembers,
|
||||
} from './Styles';
|
||||
import TaskAssignee from 'shared/components/TaskAssignee';
|
||||
import { usePopup, Popup } from 'shared/components/PopupMenu';
|
||||
import MiniProfile from 'shared/components/MiniProfile';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
const HomeDashboard = styled(Home)``;
|
||||
|
||||
type ProjectHeadingProps = {
|
||||
projectName: string;
|
||||
onFavorite?: () => void;
|
||||
name: string;
|
||||
onSaveProjectName?: (projectName: string) => void;
|
||||
onOpenSettings: ($target: React.RefObject<HTMLElement>) => void;
|
||||
};
|
||||
|
||||
const ProjectHeading: React.FC<ProjectHeadingProps> = ({
|
||||
projectName: initialProjectName,
|
||||
onFavorite,
|
||||
name: initialProjectName,
|
||||
onSaveProjectName,
|
||||
onOpenSettings,
|
||||
}) => {
|
||||
@ -73,7 +80,6 @@ const ProjectHeading: React.FC<ProjectHeadingProps> = ({
|
||||
const $settings = useRef<HTMLButtonElement>(null);
|
||||
return (
|
||||
<>
|
||||
<Separator>»</Separator>
|
||||
{isEditProjectName ? (
|
||||
<ProjectNameTextarea
|
||||
ref={$projectName}
|
||||
@ -100,28 +106,54 @@ const ProjectHeading: React.FC<ProjectHeadingProps> = ({
|
||||
>
|
||||
<AngleDown color="#c2c6dc" />
|
||||
</ProjectSettingsButton>
|
||||
<ProjectSettingsButton>
|
||||
<Star width={16} height={16} color="#c2c6dc" />
|
||||
</ProjectSettingsButton>
|
||||
{onFavorite && (
|
||||
<ProjectSettingsButton onClick={() => onFavorite()}>
|
||||
<Star width={16} height={16} color="#c2c6dc" />
|
||||
</ProjectSettingsButton>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
type MenuType = {
|
||||
[key: number]: string;
|
||||
};
|
||||
type MenuTypes = {
|
||||
[key: string]: Array<string>;
|
||||
};
|
||||
|
||||
export const MENU_TYPES: MenuTypes = {
|
||||
PROJECT_MENU: ['Board', 'Timeline', 'Calender'],
|
||||
TEAM_MENU: ['Projects', 'Members', 'Settings'],
|
||||
};
|
||||
|
||||
type NavBarProps = {
|
||||
projectName: string | null;
|
||||
menuType?: Array<string> | null;
|
||||
name: string | null;
|
||||
currentTab?: number;
|
||||
onOpenProjectFinder: ($target: React.RefObject<HTMLElement>) => void;
|
||||
onFavorite?: () => void;
|
||||
onProfileClick: ($target: React.RefObject<HTMLElement>) => void;
|
||||
onSaveProjectName?: (projectName: string) => void;
|
||||
onTabClick?: (tab: number) => void;
|
||||
onSaveName?: (name: string) => void;
|
||||
onNotificationClick: () => void;
|
||||
onDashboardClick: () => void;
|
||||
user: TaskUser | null;
|
||||
onOpenSettings: ($target: React.RefObject<HTMLElement>) => void;
|
||||
projectMembers?: Array<TaskUser> | null;
|
||||
};
|
||||
|
||||
const NavBar: React.FC<NavBarProps> = ({
|
||||
projectName,
|
||||
onSaveProjectName,
|
||||
menuType,
|
||||
currentTab,
|
||||
onOpenProjectFinder,
|
||||
onFavorite,
|
||||
onTabClick,
|
||||
name,
|
||||
onSaveName,
|
||||
onProfileClick,
|
||||
onNotificationClick,
|
||||
onDashboardClick,
|
||||
user,
|
||||
projectMembers,
|
||||
onOpenSettings,
|
||||
@ -152,45 +184,61 @@ const NavBar: React.FC<NavBarProps> = ({
|
||||
<NavbarHeader>
|
||||
<ProjectActions>
|
||||
<ProjectMeta>
|
||||
<ProjectSwitcher>Projects</ProjectSwitcher>
|
||||
{projectName && (
|
||||
{name && (
|
||||
<ProjectHeading
|
||||
onFavorite={onFavorite}
|
||||
onOpenSettings={onOpenSettings}
|
||||
projectName={projectName}
|
||||
onSaveProjectName={onSaveProjectName}
|
||||
name={name}
|
||||
onSaveProjectName={onSaveName}
|
||||
/>
|
||||
)}
|
||||
</ProjectMeta>
|
||||
{projectName && (
|
||||
{name && (
|
||||
<ProjectTabs>
|
||||
<ProjectTab active>Board</ProjectTab>
|
||||
<ProjectTab>Calender</ProjectTab>
|
||||
<ProjectTab>Timeline</ProjectTab>
|
||||
<ProjectTab>Wiki</ProjectTab>
|
||||
{menuType &&
|
||||
menuType.map((name, idx) => {
|
||||
console.log(`${name} : ${idx} === ${currentTab}`);
|
||||
return <ProjectTab active={currentTab === idx}>{name}</ProjectTab>;
|
||||
})}
|
||||
</ProjectTabs>
|
||||
)}
|
||||
</ProjectActions>
|
||||
<LogoContainer to="/">
|
||||
<CitadelLogo width={24} height={24} />
|
||||
<CitadelTitle>Citadel</CitadelTitle>
|
||||
</LogoContainer>
|
||||
<GlobalActions>
|
||||
{projectMembers && (
|
||||
<ProjectMembers>
|
||||
{projectMembers.map(member => (
|
||||
<TaskAssignee key={member.id} size={28} member={member} onMemberProfile={onMemberProfile} />
|
||||
))}
|
||||
<InviteButton variant="outline">Invite</InviteButton>
|
||||
</ProjectMembers>
|
||||
<>
|
||||
<ProjectMembers>
|
||||
{projectMembers.map(member => (
|
||||
<TaskAssignee key={member.id} size={28} member={member} onMemberProfile={onMemberProfile} />
|
||||
))}
|
||||
<InviteButton variant="outline">Invite</InviteButton>
|
||||
</ProjectMembers>
|
||||
<NavSeparator />
|
||||
</>
|
||||
)}
|
||||
<NotificationContainer onClick={onNotificationClick}>
|
||||
<ProjectFinder onClick={onOpenProjectFinder} variant="gradient">
|
||||
Projects
|
||||
</ProjectFinder>
|
||||
<IconContainer onClick={onDashboardClick}>
|
||||
<HomeDashboard width={20} height={20} />
|
||||
</IconContainer>
|
||||
<IconContainer>
|
||||
<CheckCircle width={20} height={20} />
|
||||
</IconContainer>
|
||||
<IconContainer onClick={onNotificationClick}>
|
||||
<Bell color="#c2c6dc" size={20} />
|
||||
</NotificationContainer>
|
||||
</IconContainer>
|
||||
<IconContainer>
|
||||
<BarChart width={20} height={20} />
|
||||
</IconContainer>
|
||||
|
||||
{user && (
|
||||
<ProfileContainer>
|
||||
<ProfileNameWrapper>
|
||||
<ProfileNamePrimary>{user.fullName}</ProfileNamePrimary>
|
||||
<ProfileNameSecondary>Manager</ProfileNameSecondary>
|
||||
</ProfileNameWrapper>
|
||||
<ProfileIcon user={user} size={40} onProfileClick={handleProfileClick} />}
|
||||
</ProfileContainer>
|
||||
<IconContainer>
|
||||
<ProfileIcon user={user} size={30} onProfileClick={handleProfileClick} />
|
||||
</IconContainer>
|
||||
)}
|
||||
</GlobalActions>
|
||||
</NavbarHeader>
|
||||
|
@ -131,7 +131,7 @@ export type Task = {
|
||||
};
|
||||
|
||||
export type ProjectsFilter = {
|
||||
teamID?: Maybe<Scalars['String']>;
|
||||
teamID?: Maybe<Scalars['UUID']>;
|
||||
};
|
||||
|
||||
export type FindUser = {
|
||||
@ -152,6 +152,10 @@ export type Organization = {
|
||||
name: Scalars['String'];
|
||||
};
|
||||
|
||||
export type FindTeam = {
|
||||
teamID: Scalars['UUID'];
|
||||
};
|
||||
|
||||
export type Query = {
|
||||
__typename?: 'Query';
|
||||
organizations: Array<Organization>;
|
||||
@ -160,6 +164,7 @@ export type Query = {
|
||||
findProject: Project;
|
||||
findTask: Task;
|
||||
projects: Array<Project>;
|
||||
findTeam: Team;
|
||||
teams: Array<Team>;
|
||||
labelColors: Array<LabelColor>;
|
||||
taskGroups: Array<TaskGroup>;
|
||||
@ -186,6 +191,11 @@ export type QueryProjectsArgs = {
|
||||
input?: Maybe<ProjectsFilter>;
|
||||
};
|
||||
|
||||
|
||||
export type QueryFindTeamArgs = {
|
||||
input: FindTeam;
|
||||
};
|
||||
|
||||
export type NewRefreshToken = {
|
||||
userId: Scalars['String'];
|
||||
};
|
||||
@ -419,10 +429,48 @@ export type DeleteProjectPayload = {
|
||||
project: Project;
|
||||
};
|
||||
|
||||
export type DeleteTeam = {
|
||||
teamID: Scalars['UUID'];
|
||||
};
|
||||
|
||||
export type DeleteTeamPayload = {
|
||||
__typename?: 'DeleteTeamPayload';
|
||||
ok: Scalars['Boolean'];
|
||||
team: Team;
|
||||
projects: Array<Project>;
|
||||
};
|
||||
|
||||
export type DeleteUserAccount = {
|
||||
userID: Scalars['UUID'];
|
||||
};
|
||||
|
||||
export type DeleteUserAccountPayload = {
|
||||
__typename?: 'DeleteUserAccountPayload';
|
||||
ok: Scalars['Boolean'];
|
||||
userAccount: UserAccount;
|
||||
};
|
||||
|
||||
export type UpdateTaskChecklistName = {
|
||||
taskChecklistID: Scalars['UUID'];
|
||||
name: Scalars['String'];
|
||||
};
|
||||
|
||||
export type DeleteTaskChecklist = {
|
||||
taskChecklistID: Scalars['UUID'];
|
||||
};
|
||||
|
||||
export type DeleteTaskChecklistPayload = {
|
||||
__typename?: 'DeleteTaskChecklistPayload';
|
||||
ok: Scalars['Boolean'];
|
||||
taskChecklist: TaskChecklist;
|
||||
};
|
||||
|
||||
export type Mutation = {
|
||||
__typename?: 'Mutation';
|
||||
createRefreshToken: RefreshToken;
|
||||
createUserAccount: UserAccount;
|
||||
deleteUserAccount: DeleteUserAccountPayload;
|
||||
deleteTeam: DeleteTeamPayload;
|
||||
createTeam: Team;
|
||||
clearProfileAvatar: UserAccount;
|
||||
createTeamMember: CreateTeamMemberPayload;
|
||||
@ -442,6 +490,8 @@ export type Mutation = {
|
||||
removeTaskLabel: Task;
|
||||
toggleTaskLabel: ToggleTaskLabelPayload;
|
||||
createTaskChecklist: TaskChecklist;
|
||||
deleteTaskChecklist: DeleteTaskChecklistPayload;
|
||||
updateTaskChecklistName: TaskChecklist;
|
||||
createTaskChecklistItem: TaskChecklistItem;
|
||||
updateTaskChecklistItemName: TaskChecklistItem;
|
||||
setTaskChecklistItemComplete: TaskChecklistItem;
|
||||
@ -469,6 +519,16 @@ export type MutationCreateUserAccountArgs = {
|
||||
};
|
||||
|
||||
|
||||
export type MutationDeleteUserAccountArgs = {
|
||||
input: DeleteUserAccount;
|
||||
};
|
||||
|
||||
|
||||
export type MutationDeleteTeamArgs = {
|
||||
input: DeleteTeam;
|
||||
};
|
||||
|
||||
|
||||
export type MutationCreateTeamArgs = {
|
||||
input: NewTeam;
|
||||
};
|
||||
@ -559,6 +619,16 @@ export type MutationCreateTaskChecklistArgs = {
|
||||
};
|
||||
|
||||
|
||||
export type MutationDeleteTaskChecklistArgs = {
|
||||
input: DeleteTaskChecklist;
|
||||
};
|
||||
|
||||
|
||||
export type MutationUpdateTaskChecklistNameArgs = {
|
||||
input: UpdateTaskChecklistName;
|
||||
};
|
||||
|
||||
|
||||
export type MutationCreateTaskChecklistItemArgs = {
|
||||
input: CreateTaskChecklistItem;
|
||||
};
|
||||
@ -699,43 +769,6 @@ export type CreateProjectLabelMutation = (
|
||||
) }
|
||||
);
|
||||
|
||||
export type CreateTaskMutationVariables = {
|
||||
taskGroupID: Scalars['String'];
|
||||
name: Scalars['String'];
|
||||
position: Scalars['Float'];
|
||||
};
|
||||
|
||||
|
||||
export type CreateTaskMutation = (
|
||||
{ __typename?: 'Mutation' }
|
||||
& { createTask: (
|
||||
{ __typename?: 'Task' }
|
||||
& Pick<Task, 'id' | 'name' | 'position' | 'description' | 'dueDate'>
|
||||
& { taskGroup: (
|
||||
{ __typename?: 'TaskGroup' }
|
||||
& Pick<TaskGroup, 'id' | 'name' | 'position'>
|
||||
), labels: Array<(
|
||||
{ __typename?: 'TaskLabel' }
|
||||
& Pick<TaskLabel, 'id' | 'assignedDate'>
|
||||
& { projectLabel: (
|
||||
{ __typename?: 'ProjectLabel' }
|
||||
& Pick<ProjectLabel, 'id' | 'name' | 'createdDate'>
|
||||
& { labelColor: (
|
||||
{ __typename?: 'LabelColor' }
|
||||
& Pick<LabelColor, 'id' | 'colorHex' | 'position' | 'name'>
|
||||
) }
|
||||
) }
|
||||
)>, assigned: Array<(
|
||||
{ __typename?: 'ProjectMember' }
|
||||
& Pick<ProjectMember, 'id' | 'fullName'>
|
||||
& { profileIcon: (
|
||||
{ __typename?: 'ProfileIcon' }
|
||||
& Pick<ProfileIcon, 'url' | 'initials' | 'bgColor'>
|
||||
) }
|
||||
)> }
|
||||
) }
|
||||
);
|
||||
|
||||
export type CreateTaskGroupMutationVariables = {
|
||||
projectID: Scalars['String'];
|
||||
name: Scalars['String'];
|
||||
@ -849,6 +882,12 @@ export type FindTaskQuery = (
|
||||
& { taskGroup: (
|
||||
{ __typename?: 'TaskGroup' }
|
||||
& Pick<TaskGroup, 'id'>
|
||||
), badges: (
|
||||
{ __typename?: 'TaskBadges' }
|
||||
& { checklist?: Maybe<(
|
||||
{ __typename?: 'ChecklistBadge' }
|
||||
& Pick<ChecklistBadge, 'total' | 'complete'>
|
||||
)> }
|
||||
), checklists: Array<(
|
||||
{ __typename?: 'TaskChecklist' }
|
||||
& Pick<TaskChecklist, 'id' | 'name' | 'position'>
|
||||
@ -881,9 +920,15 @@ export type FindTaskQuery = (
|
||||
export type TaskFieldsFragment = (
|
||||
{ __typename?: 'Task' }
|
||||
& Pick<Task, 'id' | 'name' | 'description' | 'dueDate' | 'complete' | 'position'>
|
||||
& { taskGroup: (
|
||||
& { badges: (
|
||||
{ __typename?: 'TaskBadges' }
|
||||
& { checklist?: Maybe<(
|
||||
{ __typename?: 'ChecklistBadge' }
|
||||
& Pick<ChecklistBadge, 'complete' | 'total'>
|
||||
)> }
|
||||
), taskGroup: (
|
||||
{ __typename?: 'TaskGroup' }
|
||||
& Pick<TaskGroup, 'id'>
|
||||
& Pick<TaskGroup, 'id' | 'name' | 'position'>
|
||||
), labels: Array<(
|
||||
{ __typename?: 'TaskLabel' }
|
||||
& Pick<TaskLabel, 'id' | 'assignedDate'>
|
||||
@ -958,6 +1003,40 @@ export type DeleteProjectMutation = (
|
||||
) }
|
||||
);
|
||||
|
||||
export type CreateTaskMutationVariables = {
|
||||
taskGroupID: Scalars['String'];
|
||||
name: Scalars['String'];
|
||||
position: Scalars['Float'];
|
||||
};
|
||||
|
||||
|
||||
export type CreateTaskMutation = (
|
||||
{ __typename?: 'Mutation' }
|
||||
& { createTask: (
|
||||
{ __typename?: 'Task' }
|
||||
& TaskFieldsFragment
|
||||
) }
|
||||
);
|
||||
|
||||
export type CreateTaskChecklistMutationVariables = {
|
||||
taskID: Scalars['UUID'];
|
||||
name: Scalars['String'];
|
||||
position: Scalars['Float'];
|
||||
};
|
||||
|
||||
|
||||
export type CreateTaskChecklistMutation = (
|
||||
{ __typename?: 'Mutation' }
|
||||
& { createTaskChecklist: (
|
||||
{ __typename?: 'TaskChecklist' }
|
||||
& Pick<TaskChecklist, 'id' | 'name' | 'position'>
|
||||
& { items: Array<(
|
||||
{ __typename?: 'TaskChecklistItem' }
|
||||
& Pick<TaskChecklistItem, 'id' | 'name' | 'taskChecklistID' | 'complete' | 'position'>
|
||||
)> }
|
||||
) }
|
||||
);
|
||||
|
||||
export type CreateTaskChecklistItemMutationVariables = {
|
||||
taskChecklistID: Scalars['UUID'];
|
||||
name: Scalars['String'];
|
||||
@ -973,6 +1052,23 @@ export type CreateTaskChecklistItemMutation = (
|
||||
) }
|
||||
);
|
||||
|
||||
export type DeleteTaskChecklistMutationVariables = {
|
||||
taskChecklistID: Scalars['UUID'];
|
||||
};
|
||||
|
||||
|
||||
export type DeleteTaskChecklistMutation = (
|
||||
{ __typename?: 'Mutation' }
|
||||
& { deleteTaskChecklist: (
|
||||
{ __typename?: 'DeleteTaskChecklistPayload' }
|
||||
& Pick<DeleteTaskChecklistPayload, 'ok'>
|
||||
& { taskChecklist: (
|
||||
{ __typename?: 'TaskChecklist' }
|
||||
& Pick<TaskChecklist, 'id'>
|
||||
) }
|
||||
) }
|
||||
);
|
||||
|
||||
export type DeleteTaskChecklistItemMutationVariables = {
|
||||
taskChecklistItemID: Scalars['UUID'];
|
||||
};
|
||||
@ -1000,7 +1096,7 @@ export type SetTaskChecklistItemCompleteMutation = (
|
||||
{ __typename?: 'Mutation' }
|
||||
& { setTaskChecklistItemComplete: (
|
||||
{ __typename?: 'TaskChecklistItem' }
|
||||
& Pick<TaskChecklistItem, 'id' | 'name' | 'taskChecklistID' | 'complete' | 'position'>
|
||||
& Pick<TaskChecklistItem, 'id' | 'complete'>
|
||||
) }
|
||||
);
|
||||
|
||||
@ -1032,6 +1128,24 @@ export type UpdateTaskChecklistItemNameMutation = (
|
||||
) }
|
||||
);
|
||||
|
||||
export type UpdateTaskChecklistNameMutationVariables = {
|
||||
taskChecklistID: Scalars['UUID'];
|
||||
name: Scalars['String'];
|
||||
};
|
||||
|
||||
|
||||
export type UpdateTaskChecklistNameMutation = (
|
||||
{ __typename?: 'Mutation' }
|
||||
& { updateTaskChecklistName: (
|
||||
{ __typename?: 'TaskChecklist' }
|
||||
& Pick<TaskChecklist, 'id' | 'name' | 'position'>
|
||||
& { items: Array<(
|
||||
{ __typename?: 'TaskChecklistItem' }
|
||||
& Pick<TaskChecklistItem, 'id' | 'name' | 'taskChecklistID' | 'complete' | 'position'>
|
||||
)> }
|
||||
) }
|
||||
);
|
||||
|
||||
export type UpdateTaskGroupNameMutationVariables = {
|
||||
taskGroupID: Scalars['UUID'];
|
||||
name: Scalars['String'];
|
||||
@ -1060,6 +1174,43 @@ export type CreateTeamMutation = (
|
||||
) }
|
||||
);
|
||||
|
||||
export type DeleteTeamMutationVariables = {
|
||||
teamID: Scalars['UUID'];
|
||||
};
|
||||
|
||||
|
||||
export type DeleteTeamMutation = (
|
||||
{ __typename?: 'Mutation' }
|
||||
& { deleteTeam: (
|
||||
{ __typename?: 'DeleteTeamPayload' }
|
||||
& Pick<DeleteTeamPayload, 'ok'>
|
||||
& { team: (
|
||||
{ __typename?: 'Team' }
|
||||
& Pick<Team, 'id'>
|
||||
) }
|
||||
) }
|
||||
);
|
||||
|
||||
export type GetTeamQueryVariables = {
|
||||
teamID: Scalars['UUID'];
|
||||
};
|
||||
|
||||
|
||||
export type GetTeamQuery = (
|
||||
{ __typename?: 'Query' }
|
||||
& { findTeam: (
|
||||
{ __typename?: 'Team' }
|
||||
& Pick<Team, 'id' | 'createdAt' | 'name'>
|
||||
), projects: Array<(
|
||||
{ __typename?: 'Project' }
|
||||
& Pick<Project, 'id' | 'name'>
|
||||
& { team: (
|
||||
{ __typename?: 'Team' }
|
||||
& Pick<Team, 'id' | 'name'>
|
||||
) }
|
||||
)> }
|
||||
);
|
||||
|
||||
export type ToggleTaskLabelMutationVariables = {
|
||||
taskID: Scalars['UUID'];
|
||||
projectLabelID: Scalars['UUID'];
|
||||
@ -1220,6 +1371,42 @@ export type UpdateTaskNameMutation = (
|
||||
) }
|
||||
);
|
||||
|
||||
export type CreateUserAccountMutationVariables = {
|
||||
username: Scalars['String'];
|
||||
email: Scalars['String'];
|
||||
fullName: Scalars['String'];
|
||||
initials: Scalars['String'];
|
||||
password: Scalars['String'];
|
||||
};
|
||||
|
||||
|
||||
export type CreateUserAccountMutation = (
|
||||
{ __typename?: 'Mutation' }
|
||||
& { createUserAccount: (
|
||||
{ __typename?: 'UserAccount' }
|
||||
& Pick<UserAccount, 'id' | 'email' | 'fullName' | 'initials' | 'username'>
|
||||
& { profileIcon: (
|
||||
{ __typename?: 'ProfileIcon' }
|
||||
& Pick<ProfileIcon, 'url' | 'initials' | 'bgColor'>
|
||||
) }
|
||||
) }
|
||||
);
|
||||
|
||||
export type UsersQueryVariables = {};
|
||||
|
||||
|
||||
export type UsersQuery = (
|
||||
{ __typename?: 'Query' }
|
||||
& { users: Array<(
|
||||
{ __typename?: 'UserAccount' }
|
||||
& Pick<UserAccount, 'id' | 'email' | 'fullName' | 'username'>
|
||||
& { profileIcon: (
|
||||
{ __typename?: 'ProfileIcon' }
|
||||
& Pick<ProfileIcon, 'url' | 'initials' | 'bgColor'>
|
||||
) }
|
||||
)> }
|
||||
);
|
||||
|
||||
export const TaskFieldsFragmentDoc = gql`
|
||||
fragment TaskFields on Task {
|
||||
id
|
||||
@ -1228,8 +1415,16 @@ export const TaskFieldsFragmentDoc = gql`
|
||||
dueDate
|
||||
complete
|
||||
position
|
||||
badges {
|
||||
checklist {
|
||||
complete
|
||||
total
|
||||
}
|
||||
}
|
||||
taskGroup {
|
||||
id
|
||||
name
|
||||
position
|
||||
}
|
||||
labels {
|
||||
id
|
||||
@ -1412,73 +1607,6 @@ export function useCreateProjectLabelMutation(baseOptions?: ApolloReactHooks.Mut
|
||||
export type CreateProjectLabelMutationHookResult = ReturnType<typeof useCreateProjectLabelMutation>;
|
||||
export type CreateProjectLabelMutationResult = ApolloReactCommon.MutationResult<CreateProjectLabelMutation>;
|
||||
export type CreateProjectLabelMutationOptions = ApolloReactCommon.BaseMutationOptions<CreateProjectLabelMutation, CreateProjectLabelMutationVariables>;
|
||||
export const CreateTaskDocument = gql`
|
||||
mutation createTask($taskGroupID: String!, $name: String!, $position: Float!) {
|
||||
createTask(input: {taskGroupID: $taskGroupID, name: $name, position: $position}) {
|
||||
id
|
||||
name
|
||||
position
|
||||
description
|
||||
dueDate
|
||||
taskGroup {
|
||||
id
|
||||
name
|
||||
position
|
||||
}
|
||||
labels {
|
||||
id
|
||||
assignedDate
|
||||
projectLabel {
|
||||
id
|
||||
name
|
||||
createdDate
|
||||
labelColor {
|
||||
id
|
||||
colorHex
|
||||
position
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
assigned {
|
||||
id
|
||||
fullName
|
||||
profileIcon {
|
||||
url
|
||||
initials
|
||||
bgColor
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
export type CreateTaskMutationFn = ApolloReactCommon.MutationFunction<CreateTaskMutation, CreateTaskMutationVariables>;
|
||||
|
||||
/**
|
||||
* __useCreateTaskMutation__
|
||||
*
|
||||
* To run a mutation, you first call `useCreateTaskMutation` within a React component and pass it any options that fit your needs.
|
||||
* When your component renders, `useCreateTaskMutation` returns a tuple that includes:
|
||||
* - A mutate function that you can call at any time to execute the mutation
|
||||
* - An object with fields that represent the current status of the mutation's execution
|
||||
*
|
||||
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
||||
*
|
||||
* @example
|
||||
* const [createTaskMutation, { data, loading, error }] = useCreateTaskMutation({
|
||||
* variables: {
|
||||
* taskGroupID: // value for 'taskGroupID'
|
||||
* name: // value for 'name'
|
||||
* position: // value for 'position'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useCreateTaskMutation(baseOptions?: ApolloReactHooks.MutationHookOptions<CreateTaskMutation, CreateTaskMutationVariables>) {
|
||||
return ApolloReactHooks.useMutation<CreateTaskMutation, CreateTaskMutationVariables>(CreateTaskDocument, baseOptions);
|
||||
}
|
||||
export type CreateTaskMutationHookResult = ReturnType<typeof useCreateTaskMutation>;
|
||||
export type CreateTaskMutationResult = ApolloReactCommon.MutationResult<CreateTaskMutation>;
|
||||
export type CreateTaskMutationOptions = ApolloReactCommon.BaseMutationOptions<CreateTaskMutation, CreateTaskMutationVariables>;
|
||||
export const CreateTaskGroupDocument = gql`
|
||||
mutation createTaskGroup($projectID: String!, $name: String!, $position: Float!) {
|
||||
createTaskGroup(input: {projectID: $projectID, name: $name, position: $position}) {
|
||||
@ -1698,6 +1826,12 @@ export const FindTaskDocument = gql`
|
||||
taskGroup {
|
||||
id
|
||||
}
|
||||
badges {
|
||||
checklist {
|
||||
total
|
||||
complete
|
||||
}
|
||||
}
|
||||
checklists {
|
||||
id
|
||||
name
|
||||
@ -1882,6 +2016,83 @@ export function useDeleteProjectMutation(baseOptions?: ApolloReactHooks.Mutation
|
||||
export type DeleteProjectMutationHookResult = ReturnType<typeof useDeleteProjectMutation>;
|
||||
export type DeleteProjectMutationResult = ApolloReactCommon.MutationResult<DeleteProjectMutation>;
|
||||
export type DeleteProjectMutationOptions = ApolloReactCommon.BaseMutationOptions<DeleteProjectMutation, DeleteProjectMutationVariables>;
|
||||
export const CreateTaskDocument = gql`
|
||||
mutation createTask($taskGroupID: String!, $name: String!, $position: Float!) {
|
||||
createTask(input: {taskGroupID: $taskGroupID, name: $name, position: $position}) {
|
||||
...TaskFields
|
||||
}
|
||||
}
|
||||
${TaskFieldsFragmentDoc}`;
|
||||
export type CreateTaskMutationFn = ApolloReactCommon.MutationFunction<CreateTaskMutation, CreateTaskMutationVariables>;
|
||||
|
||||
/**
|
||||
* __useCreateTaskMutation__
|
||||
*
|
||||
* To run a mutation, you first call `useCreateTaskMutation` within a React component and pass it any options that fit your needs.
|
||||
* When your component renders, `useCreateTaskMutation` returns a tuple that includes:
|
||||
* - A mutate function that you can call at any time to execute the mutation
|
||||
* - An object with fields that represent the current status of the mutation's execution
|
||||
*
|
||||
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
||||
*
|
||||
* @example
|
||||
* const [createTaskMutation, { data, loading, error }] = useCreateTaskMutation({
|
||||
* variables: {
|
||||
* taskGroupID: // value for 'taskGroupID'
|
||||
* name: // value for 'name'
|
||||
* position: // value for 'position'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useCreateTaskMutation(baseOptions?: ApolloReactHooks.MutationHookOptions<CreateTaskMutation, CreateTaskMutationVariables>) {
|
||||
return ApolloReactHooks.useMutation<CreateTaskMutation, CreateTaskMutationVariables>(CreateTaskDocument, baseOptions);
|
||||
}
|
||||
export type CreateTaskMutationHookResult = ReturnType<typeof useCreateTaskMutation>;
|
||||
export type CreateTaskMutationResult = ApolloReactCommon.MutationResult<CreateTaskMutation>;
|
||||
export type CreateTaskMutationOptions = ApolloReactCommon.BaseMutationOptions<CreateTaskMutation, CreateTaskMutationVariables>;
|
||||
export const CreateTaskChecklistDocument = gql`
|
||||
mutation createTaskChecklist($taskID: UUID!, $name: String!, $position: Float!) {
|
||||
createTaskChecklist(input: {taskID: $taskID, name: $name, position: $position}) {
|
||||
id
|
||||
name
|
||||
position
|
||||
items {
|
||||
id
|
||||
name
|
||||
taskChecklistID
|
||||
complete
|
||||
position
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
export type CreateTaskChecklistMutationFn = ApolloReactCommon.MutationFunction<CreateTaskChecklistMutation, CreateTaskChecklistMutationVariables>;
|
||||
|
||||
/**
|
||||
* __useCreateTaskChecklistMutation__
|
||||
*
|
||||
* To run a mutation, you first call `useCreateTaskChecklistMutation` within a React component and pass it any options that fit your needs.
|
||||
* When your component renders, `useCreateTaskChecklistMutation` returns a tuple that includes:
|
||||
* - A mutate function that you can call at any time to execute the mutation
|
||||
* - An object with fields that represent the current status of the mutation's execution
|
||||
*
|
||||
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
||||
*
|
||||
* @example
|
||||
* const [createTaskChecklistMutation, { data, loading, error }] = useCreateTaskChecklistMutation({
|
||||
* variables: {
|
||||
* taskID: // value for 'taskID'
|
||||
* name: // value for 'name'
|
||||
* position: // value for 'position'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useCreateTaskChecklistMutation(baseOptions?: ApolloReactHooks.MutationHookOptions<CreateTaskChecklistMutation, CreateTaskChecklistMutationVariables>) {
|
||||
return ApolloReactHooks.useMutation<CreateTaskChecklistMutation, CreateTaskChecklistMutationVariables>(CreateTaskChecklistDocument, baseOptions);
|
||||
}
|
||||
export type CreateTaskChecklistMutationHookResult = ReturnType<typeof useCreateTaskChecklistMutation>;
|
||||
export type CreateTaskChecklistMutationResult = ApolloReactCommon.MutationResult<CreateTaskChecklistMutation>;
|
||||
export type CreateTaskChecklistMutationOptions = ApolloReactCommon.BaseMutationOptions<CreateTaskChecklistMutation, CreateTaskChecklistMutationVariables>;
|
||||
export const CreateTaskChecklistItemDocument = gql`
|
||||
mutation createTaskChecklistItem($taskChecklistID: UUID!, $name: String!, $position: Float!) {
|
||||
createTaskChecklistItem(input: {taskChecklistID: $taskChecklistID, name: $name, position: $position}) {
|
||||
@ -1920,6 +2131,41 @@ export function useCreateTaskChecklistItemMutation(baseOptions?: ApolloReactHook
|
||||
export type CreateTaskChecklistItemMutationHookResult = ReturnType<typeof useCreateTaskChecklistItemMutation>;
|
||||
export type CreateTaskChecklistItemMutationResult = ApolloReactCommon.MutationResult<CreateTaskChecklistItemMutation>;
|
||||
export type CreateTaskChecklistItemMutationOptions = ApolloReactCommon.BaseMutationOptions<CreateTaskChecklistItemMutation, CreateTaskChecklistItemMutationVariables>;
|
||||
export const DeleteTaskChecklistDocument = gql`
|
||||
mutation deleteTaskChecklist($taskChecklistID: UUID!) {
|
||||
deleteTaskChecklist(input: {taskChecklistID: $taskChecklistID}) {
|
||||
ok
|
||||
taskChecklist {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
export type DeleteTaskChecklistMutationFn = ApolloReactCommon.MutationFunction<DeleteTaskChecklistMutation, DeleteTaskChecklistMutationVariables>;
|
||||
|
||||
/**
|
||||
* __useDeleteTaskChecklistMutation__
|
||||
*
|
||||
* To run a mutation, you first call `useDeleteTaskChecklistMutation` within a React component and pass it any options that fit your needs.
|
||||
* When your component renders, `useDeleteTaskChecklistMutation` returns a tuple that includes:
|
||||
* - A mutate function that you can call at any time to execute the mutation
|
||||
* - An object with fields that represent the current status of the mutation's execution
|
||||
*
|
||||
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
||||
*
|
||||
* @example
|
||||
* const [deleteTaskChecklistMutation, { data, loading, error }] = useDeleteTaskChecklistMutation({
|
||||
* variables: {
|
||||
* taskChecklistID: // value for 'taskChecklistID'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useDeleteTaskChecklistMutation(baseOptions?: ApolloReactHooks.MutationHookOptions<DeleteTaskChecklistMutation, DeleteTaskChecklistMutationVariables>) {
|
||||
return ApolloReactHooks.useMutation<DeleteTaskChecklistMutation, DeleteTaskChecklistMutationVariables>(DeleteTaskChecklistDocument, baseOptions);
|
||||
}
|
||||
export type DeleteTaskChecklistMutationHookResult = ReturnType<typeof useDeleteTaskChecklistMutation>;
|
||||
export type DeleteTaskChecklistMutationResult = ApolloReactCommon.MutationResult<DeleteTaskChecklistMutation>;
|
||||
export type DeleteTaskChecklistMutationOptions = ApolloReactCommon.BaseMutationOptions<DeleteTaskChecklistMutation, DeleteTaskChecklistMutationVariables>;
|
||||
export const DeleteTaskChecklistItemDocument = gql`
|
||||
mutation deleteTaskChecklistItem($taskChecklistItemID: UUID!) {
|
||||
deleteTaskChecklistItem(input: {taskChecklistItemID: $taskChecklistItemID}) {
|
||||
@ -1960,10 +2206,7 @@ export const SetTaskChecklistItemCompleteDocument = gql`
|
||||
mutation setTaskChecklistItemComplete($taskChecklistItemID: UUID!, $complete: Boolean!) {
|
||||
setTaskChecklistItemComplete(input: {taskChecklistItemID: $taskChecklistItemID, complete: $complete}) {
|
||||
id
|
||||
name
|
||||
taskChecklistID
|
||||
complete
|
||||
position
|
||||
}
|
||||
}
|
||||
`;
|
||||
@ -2060,6 +2303,48 @@ export function useUpdateTaskChecklistItemNameMutation(baseOptions?: ApolloReact
|
||||
export type UpdateTaskChecklistItemNameMutationHookResult = ReturnType<typeof useUpdateTaskChecklistItemNameMutation>;
|
||||
export type UpdateTaskChecklistItemNameMutationResult = ApolloReactCommon.MutationResult<UpdateTaskChecklistItemNameMutation>;
|
||||
export type UpdateTaskChecklistItemNameMutationOptions = ApolloReactCommon.BaseMutationOptions<UpdateTaskChecklistItemNameMutation, UpdateTaskChecklistItemNameMutationVariables>;
|
||||
export const UpdateTaskChecklistNameDocument = gql`
|
||||
mutation updateTaskChecklistName($taskChecklistID: UUID!, $name: String!) {
|
||||
updateTaskChecklistName(input: {taskChecklistID: $taskChecklistID, name: $name}) {
|
||||
id
|
||||
name
|
||||
position
|
||||
items {
|
||||
id
|
||||
name
|
||||
taskChecklistID
|
||||
complete
|
||||
position
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
export type UpdateTaskChecklistNameMutationFn = ApolloReactCommon.MutationFunction<UpdateTaskChecklistNameMutation, UpdateTaskChecklistNameMutationVariables>;
|
||||
|
||||
/**
|
||||
* __useUpdateTaskChecklistNameMutation__
|
||||
*
|
||||
* To run a mutation, you first call `useUpdateTaskChecklistNameMutation` within a React component and pass it any options that fit your needs.
|
||||
* When your component renders, `useUpdateTaskChecklistNameMutation` returns a tuple that includes:
|
||||
* - A mutate function that you can call at any time to execute the mutation
|
||||
* - An object with fields that represent the current status of the mutation's execution
|
||||
*
|
||||
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
||||
*
|
||||
* @example
|
||||
* const [updateTaskChecklistNameMutation, { data, loading, error }] = useUpdateTaskChecklistNameMutation({
|
||||
* variables: {
|
||||
* taskChecklistID: // value for 'taskChecklistID'
|
||||
* name: // value for 'name'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useUpdateTaskChecklistNameMutation(baseOptions?: ApolloReactHooks.MutationHookOptions<UpdateTaskChecklistNameMutation, UpdateTaskChecklistNameMutationVariables>) {
|
||||
return ApolloReactHooks.useMutation<UpdateTaskChecklistNameMutation, UpdateTaskChecklistNameMutationVariables>(UpdateTaskChecklistNameDocument, baseOptions);
|
||||
}
|
||||
export type UpdateTaskChecklistNameMutationHookResult = ReturnType<typeof useUpdateTaskChecklistNameMutation>;
|
||||
export type UpdateTaskChecklistNameMutationResult = ApolloReactCommon.MutationResult<UpdateTaskChecklistNameMutation>;
|
||||
export type UpdateTaskChecklistNameMutationOptions = ApolloReactCommon.BaseMutationOptions<UpdateTaskChecklistNameMutation, UpdateTaskChecklistNameMutationVariables>;
|
||||
export const UpdateTaskGroupNameDocument = gql`
|
||||
mutation updateTaskGroupName($taskGroupID: UUID!, $name: String!) {
|
||||
updateTaskGroupName(input: {taskGroupID: $taskGroupID, name: $name}) {
|
||||
@ -2129,6 +2414,84 @@ export function useCreateTeamMutation(baseOptions?: ApolloReactHooks.MutationHoo
|
||||
export type CreateTeamMutationHookResult = ReturnType<typeof useCreateTeamMutation>;
|
||||
export type CreateTeamMutationResult = ApolloReactCommon.MutationResult<CreateTeamMutation>;
|
||||
export type CreateTeamMutationOptions = ApolloReactCommon.BaseMutationOptions<CreateTeamMutation, CreateTeamMutationVariables>;
|
||||
export const DeleteTeamDocument = gql`
|
||||
mutation deleteTeam($teamID: UUID!) {
|
||||
deleteTeam(input: {teamID: $teamID}) {
|
||||
ok
|
||||
team {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
export type DeleteTeamMutationFn = ApolloReactCommon.MutationFunction<DeleteTeamMutation, DeleteTeamMutationVariables>;
|
||||
|
||||
/**
|
||||
* __useDeleteTeamMutation__
|
||||
*
|
||||
* To run a mutation, you first call `useDeleteTeamMutation` within a React component and pass it any options that fit your needs.
|
||||
* When your component renders, `useDeleteTeamMutation` returns a tuple that includes:
|
||||
* - A mutate function that you can call at any time to execute the mutation
|
||||
* - An object with fields that represent the current status of the mutation's execution
|
||||
*
|
||||
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
||||
*
|
||||
* @example
|
||||
* const [deleteTeamMutation, { data, loading, error }] = useDeleteTeamMutation({
|
||||
* variables: {
|
||||
* teamID: // value for 'teamID'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useDeleteTeamMutation(baseOptions?: ApolloReactHooks.MutationHookOptions<DeleteTeamMutation, DeleteTeamMutationVariables>) {
|
||||
return ApolloReactHooks.useMutation<DeleteTeamMutation, DeleteTeamMutationVariables>(DeleteTeamDocument, baseOptions);
|
||||
}
|
||||
export type DeleteTeamMutationHookResult = ReturnType<typeof useDeleteTeamMutation>;
|
||||
export type DeleteTeamMutationResult = ApolloReactCommon.MutationResult<DeleteTeamMutation>;
|
||||
export type DeleteTeamMutationOptions = ApolloReactCommon.BaseMutationOptions<DeleteTeamMutation, DeleteTeamMutationVariables>;
|
||||
export const GetTeamDocument = gql`
|
||||
query getTeam($teamID: UUID!) {
|
||||
findTeam(input: {teamID: $teamID}) {
|
||||
id
|
||||
createdAt
|
||||
name
|
||||
}
|
||||
projects(input: {teamID: $teamID}) {
|
||||
id
|
||||
name
|
||||
team {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* __useGetTeamQuery__
|
||||
*
|
||||
* To run a query within a React component, call `useGetTeamQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useGetTeamQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||
* you can use to render your UI.
|
||||
*
|
||||
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||
*
|
||||
* @example
|
||||
* const { data, loading, error } = useGetTeamQuery({
|
||||
* variables: {
|
||||
* teamID: // value for 'teamID'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useGetTeamQuery(baseOptions?: ApolloReactHooks.QueryHookOptions<GetTeamQuery, GetTeamQueryVariables>) {
|
||||
return ApolloReactHooks.useQuery<GetTeamQuery, GetTeamQueryVariables>(GetTeamDocument, baseOptions);
|
||||
}
|
||||
export function useGetTeamLazyQuery(baseOptions?: ApolloReactHooks.LazyQueryHookOptions<GetTeamQuery, GetTeamQueryVariables>) {
|
||||
return ApolloReactHooks.useLazyQuery<GetTeamQuery, GetTeamQueryVariables>(GetTeamDocument, baseOptions);
|
||||
}
|
||||
export type GetTeamQueryHookResult = ReturnType<typeof useGetTeamQuery>;
|
||||
export type GetTeamLazyQueryHookResult = ReturnType<typeof useGetTeamLazyQuery>;
|
||||
export type GetTeamQueryResult = ApolloReactCommon.QueryResult<GetTeamQuery, GetTeamQueryVariables>;
|
||||
export const ToggleTaskLabelDocument = gql`
|
||||
mutation toggleTaskLabel($taskID: UUID!, $projectLabelID: UUID!) {
|
||||
toggleTaskLabel(input: {taskID: $taskID, projectLabelID: $projectLabelID}) {
|
||||
@ -2472,4 +2835,89 @@ export function useUpdateTaskNameMutation(baseOptions?: ApolloReactHooks.Mutatio
|
||||
}
|
||||
export type UpdateTaskNameMutationHookResult = ReturnType<typeof useUpdateTaskNameMutation>;
|
||||
export type UpdateTaskNameMutationResult = ApolloReactCommon.MutationResult<UpdateTaskNameMutation>;
|
||||
export type UpdateTaskNameMutationOptions = ApolloReactCommon.BaseMutationOptions<UpdateTaskNameMutation, UpdateTaskNameMutationVariables>;
|
||||
export type UpdateTaskNameMutationOptions = ApolloReactCommon.BaseMutationOptions<UpdateTaskNameMutation, UpdateTaskNameMutationVariables>;
|
||||
export const CreateUserAccountDocument = gql`
|
||||
mutation createUserAccount($username: String!, $email: String!, $fullName: String!, $initials: String!, $password: String!) {
|
||||
createUserAccount(input: {username: $username, email: $email, fullName: $fullName, initials: $initials, password: $password}) {
|
||||
id
|
||||
email
|
||||
fullName
|
||||
initials
|
||||
username
|
||||
profileIcon {
|
||||
url
|
||||
initials
|
||||
bgColor
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
export type CreateUserAccountMutationFn = ApolloReactCommon.MutationFunction<CreateUserAccountMutation, CreateUserAccountMutationVariables>;
|
||||
|
||||
/**
|
||||
* __useCreateUserAccountMutation__
|
||||
*
|
||||
* To run a mutation, you first call `useCreateUserAccountMutation` within a React component and pass it any options that fit your needs.
|
||||
* When your component renders, `useCreateUserAccountMutation` returns a tuple that includes:
|
||||
* - A mutate function that you can call at any time to execute the mutation
|
||||
* - An object with fields that represent the current status of the mutation's execution
|
||||
*
|
||||
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
||||
*
|
||||
* @example
|
||||
* const [createUserAccountMutation, { data, loading, error }] = useCreateUserAccountMutation({
|
||||
* variables: {
|
||||
* username: // value for 'username'
|
||||
* email: // value for 'email'
|
||||
* fullName: // value for 'fullName'
|
||||
* initials: // value for 'initials'
|
||||
* password: // value for 'password'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useCreateUserAccountMutation(baseOptions?: ApolloReactHooks.MutationHookOptions<CreateUserAccountMutation, CreateUserAccountMutationVariables>) {
|
||||
return ApolloReactHooks.useMutation<CreateUserAccountMutation, CreateUserAccountMutationVariables>(CreateUserAccountDocument, baseOptions);
|
||||
}
|
||||
export type CreateUserAccountMutationHookResult = ReturnType<typeof useCreateUserAccountMutation>;
|
||||
export type CreateUserAccountMutationResult = ApolloReactCommon.MutationResult<CreateUserAccountMutation>;
|
||||
export type CreateUserAccountMutationOptions = ApolloReactCommon.BaseMutationOptions<CreateUserAccountMutation, CreateUserAccountMutationVariables>;
|
||||
export const UsersDocument = gql`
|
||||
query users {
|
||||
users {
|
||||
id
|
||||
email
|
||||
fullName
|
||||
username
|
||||
profileIcon {
|
||||
url
|
||||
initials
|
||||
bgColor
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* __useUsersQuery__
|
||||
*
|
||||
* To run a query within a React component, call `useUsersQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useUsersQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||
* you can use to render your UI.
|
||||
*
|
||||
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||
*
|
||||
* @example
|
||||
* const { data, loading, error } = useUsersQuery({
|
||||
* variables: {
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useUsersQuery(baseOptions?: ApolloReactHooks.QueryHookOptions<UsersQuery, UsersQueryVariables>) {
|
||||
return ApolloReactHooks.useQuery<UsersQuery, UsersQueryVariables>(UsersDocument, baseOptions);
|
||||
}
|
||||
export function useUsersLazyQuery(baseOptions?: ApolloReactHooks.LazyQueryHookOptions<UsersQuery, UsersQueryVariables>) {
|
||||
return ApolloReactHooks.useLazyQuery<UsersQuery, UsersQueryVariables>(UsersDocument, baseOptions);
|
||||
}
|
||||
export type UsersQueryHookResult = ReturnType<typeof useUsersQuery>;
|
||||
export type UsersLazyQueryHookResult = ReturnType<typeof useUsersLazyQuery>;
|
||||
export type UsersQueryResult = ApolloReactCommon.QueryResult<UsersQuery, UsersQueryVariables>;
|
@ -1,38 +0,0 @@
|
||||
mutation createTask($taskGroupID: String!, $name: String!, $position: Float!) {
|
||||
createTask(input: { taskGroupID: $taskGroupID, name: $name, position: $position }) {
|
||||
id
|
||||
name
|
||||
position
|
||||
description
|
||||
dueDate
|
||||
taskGroup {
|
||||
id
|
||||
name
|
||||
position
|
||||
}
|
||||
labels {
|
||||
id
|
||||
assignedDate
|
||||
projectLabel {
|
||||
id
|
||||
name
|
||||
createdDate
|
||||
labelColor {
|
||||
id
|
||||
colorHex
|
||||
position
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
assigned {
|
||||
id
|
||||
fullName
|
||||
profileIcon {
|
||||
url
|
||||
initials
|
||||
bgColor
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -9,6 +9,12 @@ query findTask($taskID: UUID!) {
|
||||
taskGroup {
|
||||
id
|
||||
}
|
||||
badges {
|
||||
checklist {
|
||||
total
|
||||
complete
|
||||
}
|
||||
}
|
||||
checklists {
|
||||
id
|
||||
name
|
||||
|
@ -8,8 +8,16 @@ const TASK_FRAGMENT = gql`
|
||||
dueDate
|
||||
complete
|
||||
position
|
||||
badges {
|
||||
checklist {
|
||||
complete
|
||||
total
|
||||
}
|
||||
}
|
||||
taskGroup {
|
||||
id
|
||||
name
|
||||
position
|
||||
}
|
||||
labels {
|
||||
id
|
||||
|
13
web/src/shared/graphql/task/createTask.ts
Normal file
13
web/src/shared/graphql/task/createTask.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import gql from 'graphql-tag';
|
||||
import TASK_FRAGMENT from '../fragments/task';
|
||||
|
||||
const CREATE_TASK_MUTATION = gql`
|
||||
mutation createTask($taskGroupID: String!, $name: String!, $position: Float!) {
|
||||
createTask(input: { taskGroupID: $taskGroupID, name: $name, position: $position }) {
|
||||
...TaskFields
|
||||
}
|
||||
}
|
||||
${TASK_FRAGMENT}
|
||||
`;
|
||||
|
||||
export default CREATE_TASK_MUTATION;
|
20
web/src/shared/graphql/task/createTaskChecklist.ts
Normal file
20
web/src/shared/graphql/task/createTaskChecklist.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import gql from 'graphql-tag';
|
||||
|
||||
const CREATE_TASK_CHECKLIST_MUTATION = gql`
|
||||
mutation createTaskChecklist($taskID: UUID!, $name: String!, $position: Float!) {
|
||||
createTaskChecklist(input: { taskID: $taskID, name: $name, position: $position }) {
|
||||
id
|
||||
name
|
||||
position
|
||||
items {
|
||||
id
|
||||
name
|
||||
taskChecklistID
|
||||
complete
|
||||
position
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default CREATE_TASK_CHECKLIST_MUTATION;
|
14
web/src/shared/graphql/task/deleteTaskChecklist.ts
Normal file
14
web/src/shared/graphql/task/deleteTaskChecklist.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import gql from 'graphql-tag';
|
||||
|
||||
const DELETE_TASK_CHECKLIST_MUTATION = gql`
|
||||
mutation deleteTaskChecklist($taskChecklistID: UUID!) {
|
||||
deleteTaskChecklist(input: { taskChecklistID: $taskChecklistID }) {
|
||||
ok
|
||||
taskChecklist {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default DELETE_TASK_CHECKLIST_MUTATION;
|
@ -4,10 +4,7 @@ const SET_TASK_CHECKLIST_ITEM_COMPLETE = gql`
|
||||
mutation setTaskChecklistItemComplete($taskChecklistItemID: UUID!, $complete: Boolean!) {
|
||||
setTaskChecklistItemComplete(input: { taskChecklistItemID: $taskChecklistItemID, complete: $complete }) {
|
||||
id
|
||||
name
|
||||
taskChecklistID
|
||||
complete
|
||||
position
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
19
web/src/shared/graphql/task/updateTaskChecklistName.ts
Normal file
19
web/src/shared/graphql/task/updateTaskChecklistName.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import gql from 'graphql-tag';
|
||||
|
||||
const UPDATE_TASK_CHECKLIST_NAME_MUTATION = gql`
|
||||
mutation updateTaskChecklistName($taskChecklistID: UUID!, $name: String!) {
|
||||
updateTaskChecklistName(input: { taskChecklistID: $taskChecklistID, name: $name }) {
|
||||
id
|
||||
name
|
||||
position
|
||||
items {
|
||||
id
|
||||
name
|
||||
taskChecklistID
|
||||
complete
|
||||
position
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
export default UPDATE_TASK_CHECKLIST_NAME_MUTATION;
|
14
web/src/shared/graphql/team/deleteTeam.ts
Normal file
14
web/src/shared/graphql/team/deleteTeam.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import gql from 'graphql-tag';
|
||||
|
||||
export const DELETE_TEAM_MUTATION = gql`
|
||||
mutation deleteTeam($teamID: UUID!) {
|
||||
deleteTeam(input: { teamID: $teamID }) {
|
||||
ok
|
||||
team {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default DELETE_TEAM_MUTATION;
|
21
web/src/shared/graphql/team/getTeam.ts
Normal file
21
web/src/shared/graphql/team/getTeam.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import gql from 'graphql-tag';
|
||||
|
||||
export const GET_TEAM_QUERY = gql`
|
||||
query getTeam($teamID: UUID!) {
|
||||
findTeam(input: { teamID: $teamID }) {
|
||||
id
|
||||
createdAt
|
||||
name
|
||||
}
|
||||
projects(input: { teamID: $teamID }) {
|
||||
id
|
||||
name
|
||||
team {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default GET_TEAM_QUERY;
|
28
web/src/shared/graphql/user/createUser.ts
Normal file
28
web/src/shared/graphql/user/createUser.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import gql from 'graphql-tag';
|
||||
|
||||
export const CREATE_USER_MUTATION = gql`
|
||||
mutation createUserAccount(
|
||||
$username: String!
|
||||
$email: String!
|
||||
$fullName: String!
|
||||
$initials: String!
|
||||
$password: String!
|
||||
) {
|
||||
createUserAccount(
|
||||
input: { username: $username, email: $email, fullName: $fullName, initials: $initials, password: $password }
|
||||
) {
|
||||
id
|
||||
email
|
||||
fullName
|
||||
initials
|
||||
username
|
||||
profileIcon {
|
||||
url
|
||||
initials
|
||||
bgColor
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default CREATE_USER_MUTATION;
|
14
web/src/shared/graphql/users.graphqls
Normal file
14
web/src/shared/graphql/users.graphqls
Normal file
@ -0,0 +1,14 @@
|
||||
query users {
|
||||
users {
|
||||
id
|
||||
email
|
||||
fullName
|
||||
username
|
||||
profileIcon {
|
||||
url
|
||||
initials
|
||||
bgColor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
12
web/src/shared/icons/BarChart.tsx
Normal file
12
web/src/shared/icons/BarChart.tsx
Normal file
@ -0,0 +1,12 @@
|
||||
import React from 'react';
|
||||
import Icon, { IconProps } from './Icon';
|
||||
|
||||
const BarChart: React.FC<IconProps> = ({ width = '16px', height = '16px', className }) => {
|
||||
return (
|
||||
<Icon width={width} height={height} className={className} viewBox="0 0 512 512">
|
||||
<path d="M396.8 352h22.4c6.4 0 12.8-6.4 12.8-12.8V108.8c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v230.4c0 6.4 6.4 12.8 12.8 12.8zm-192 0h22.4c6.4 0 12.8-6.4 12.8-12.8V140.8c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v198.4c0 6.4 6.4 12.8 12.8 12.8zm96 0h22.4c6.4 0 12.8-6.4 12.8-12.8V204.8c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v134.4c0 6.4 6.4 12.8 12.8 12.8zM496 400H48V80c0-8.84-7.16-16-16-16H16C7.16 64 0 71.16 0 80v336c0 17.67 14.33 32 32 32h464c8.84 0 16-7.16 16-16v-16c0-8.84-7.16-16-16-16zm-387.2-48h22.4c6.4 0 12.8-6.4 12.8-12.8v-70.4c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v70.4c0 6.4 6.4 12.8 12.8 12.8z" />
|
||||
</Icon>
|
||||
);
|
||||
};
|
||||
|
||||
export default BarChart;
|
@ -1,29 +1,15 @@
|
||||
import React from 'react';
|
||||
import Icon, { IconProps } from './Icon';
|
||||
|
||||
type Props = {
|
||||
size: number | string;
|
||||
color: string;
|
||||
};
|
||||
|
||||
const Citadel = ({ size, color }: Props) => {
|
||||
const Citadel: React.FC<IconProps> = ({ width = '16px', height = '16px', className }) => {
|
||||
return (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width={size} height={size} viewBox="0 0 12.7 12.7">
|
||||
<Icon width={width} height={height} className={className} viewBox="0 0 12.7 12.7">
|
||||
<g transform="translate(-.26 -24.137) scale(.1249)">
|
||||
<path
|
||||
d="M50.886 286.515l-40.4-44.46 44.459-40.401 40.401 44.46z"
|
||||
fill="none"
|
||||
stroke={color}
|
||||
strokeWidth="11.90597031"
|
||||
/>
|
||||
<circle cx="52.917" cy="244.083" r="11.025" fill={color} />
|
||||
<path d="M50.886 286.515l-40.4-44.46 44.459-40.401 40.401 44.46z" fill="none" strokeWidth="11.90597031" />
|
||||
<circle cx="52.917" cy="244.083" r="11.025" />
|
||||
</g>
|
||||
</svg>
|
||||
</Icon>
|
||||
);
|
||||
};
|
||||
|
||||
Citadel.defaultProps = {
|
||||
size: 16,
|
||||
color: '#7367f0',
|
||||
};
|
||||
|
||||
export default Citadel;
|
||||
|
12
web/src/shared/icons/Filter.tsx
Normal file
12
web/src/shared/icons/Filter.tsx
Normal file
@ -0,0 +1,12 @@
|
||||
import React from 'react';
|
||||
import Icon, { IconProps } from './Icon';
|
||||
|
||||
const Filter: React.FC<IconProps> = ({ width = '16px', height = '16px', className }) => {
|
||||
return (
|
||||
<Icon width={width} height={height} className={className} viewBox="0 0 512 512">
|
||||
<path d="M487.976 0H24.028C2.71 0-8.047 25.866 7.058 40.971L192 225.941V432c0 7.831 3.821 15.17 10.237 19.662l80 55.98C298.02 518.69 320 507.493 320 487.98V225.941l184.947-184.97C520.021 25.896 509.338 0 487.976 0z" />
|
||||
</Icon>
|
||||
);
|
||||
};
|
||||
|
||||
export default Filter;
|
@ -1,21 +1,12 @@
|
||||
import React from 'react';
|
||||
import Icon, { IconProps } from './Icon';
|
||||
|
||||
type Props = {
|
||||
size: number | string;
|
||||
color: string;
|
||||
};
|
||||
|
||||
const Home = ({ size, color }: Props) => {
|
||||
const Checkmark: React.FC<IconProps> = ({ width = '16px', height = '16px', className }) => {
|
||||
return (
|
||||
<svg fill={color} xmlns="http://www.w3.org/2000/svg" width={size} height={size} viewBox="0 0 16 16">
|
||||
<path d="M16 9.226l-8-6.21-8 6.21v-2.532l8-6.21 8 6.21zM14 9v6h-4v-4h-4v4h-4v-6l6-4.5z" />
|
||||
</svg>
|
||||
<Icon width={width} height={height} className={className} viewBox="0 0 576 512">
|
||||
<path d="M280.37 148.26L96 300.11V464a16 16 0 0 0 16 16l112.06-.29a16 16 0 0 0 15.92-16V368a16 16 0 0 1 16-16h64a16 16 0 0 1 16 16v95.64a16 16 0 0 0 16 16.05L464 480a16 16 0 0 0 16-16V300L295.67 148.26a12.19 12.19 0 0 0-15.3 0zM571.6 251.47L488 182.56V44.05a12 12 0 0 0-12-12h-56a12 12 0 0 0-12 12v72.61L318.47 43a48 48 0 0 0-61 0L4.34 251.47a12 12 0 0 0-1.6 16.9l25.5 31A12 12 0 0 0 45.15 301l235.22-193.74a12.19 12.19 0 0 1 15.3 0L530.9 301a12 12 0 0 0 16.9-1.6l25.5-31a12 12 0 0 0-1.7-16.93z" />
|
||||
</Icon>
|
||||
);
|
||||
};
|
||||
|
||||
Home.defaultProps = {
|
||||
size: 16,
|
||||
color: '#000',
|
||||
};
|
||||
|
||||
export default Home;
|
||||
export default Checkmark;
|
||||
|
@ -18,6 +18,7 @@ type Props = {
|
||||
|
||||
const Svg = styled.svg`
|
||||
fill: rgba(${props => props.theme.colors.text.primary});
|
||||
stroke: rgba(${props => props.theme.colors.text.primary});
|
||||
`;
|
||||
|
||||
const Icon: React.FC<Props> = ({ width, height, viewBox, className, onClick, children }) => {
|
||||
|
@ -1,21 +1,12 @@
|
||||
import React from 'react';
|
||||
import Icon, { IconProps } from './Icon';
|
||||
|
||||
type Props = {
|
||||
size: number | string;
|
||||
color: string;
|
||||
};
|
||||
|
||||
const Lock = ({ size, color }: Props) => {
|
||||
const Lock: React.FC<IconProps> = ({ width = '16px', height = '16px', className }) => {
|
||||
return (
|
||||
<svg fill={color} xmlns="http://www.w3.org/2000/svg" width={size} height={size} viewBox="0 0 16 16">
|
||||
<path d="M9.25 7h-0.25v-3c0-1.654-1.346-3-3-3h-2c-1.654 0-3 1.346-3 3v3h-0.25c-0.412 0-0.75 0.338-0.75 0.75v7.5c0 0.412 0.338 0.75 0.75 0.75h8.5c0.412 0 0.75-0.338 0.75-0.75v-7.5c0-0.412-0.338-0.75-0.75-0.75zM3 4c0-0.551 0.449-1 1-1h2c0.551 0 1 0.449 1 1v3h-4v-3z" />
|
||||
</svg>
|
||||
<Icon width={width} height={height} className={className} viewBox="0 0 448 512">
|
||||
<path d="M400 224h-24v-72C376 68.2 307.8 0 224 0S72 68.2 72 152v72H48c-26.5 0-48 21.5-48 48v192c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V272c0-26.5-21.5-48-48-48zm-104 0H152v-72c0-39.7 32.3-72 72-72s72 32.3 72 72v72z" />
|
||||
</Icon>
|
||||
);
|
||||
};
|
||||
|
||||
Lock.defaultProps = {
|
||||
size: 16,
|
||||
color: '#000',
|
||||
};
|
||||
|
||||
export default Lock;
|
||||
|
@ -1,21 +1,12 @@
|
||||
import React from 'react';
|
||||
import Icon, { IconProps } from './Icon';
|
||||
|
||||
type Props = {
|
||||
size: number | string;
|
||||
color: string;
|
||||
};
|
||||
|
||||
const Pencil = ({ size, color }: Props) => {
|
||||
const Sort: React.FC<IconProps> = ({ width = '16px', height = '16px', className }) => {
|
||||
return (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill={color} width={size} height={size} viewBox="0 0 16 16">
|
||||
<path d="M13.5 0c1.381 0 2.5 1.119 2.5 2.5 0 0.563-0.186 1.082-0.5 1.5l-1 1-3.5-3.5 1-1c0.418-0.314 0.937-0.5 1.5-0.5zM1 11.5l-1 4.5 4.5-1 9.25-9.25-3.5-3.5-9.25 9.25zM11.181 5.681l-7 7-0.862-0.862 7-7 0.862 0.862z" />
|
||||
</svg>
|
||||
<Icon width={width} height={height} className={className} viewBox="0 0 512 512">
|
||||
<path d="M497.9 142.1l-46.1 46.1c-4.7 4.7-12.3 4.7-17 0l-111-111c-4.7-4.7-4.7-12.3 0-17l46.1-46.1c18.7-18.7 49.1-18.7 67.9 0l60.1 60.1c18.8 18.7 18.8 49.1 0 67.9zM284.2 99.8L21.6 362.4.4 483.9c-2.9 16.4 11.4 30.6 27.8 27.8l121.5-21.3 262.6-262.6c4.7-4.7 4.7-12.3 0-17l-111-111c-4.8-4.7-12.4-4.7-17.1 0zM124.1 339.9c-5.5-5.5-5.5-14.3 0-19.8l154-154c5.5-5.5 14.3-5.5 19.8 0s5.5 14.3 0 19.8l-154 154c-5.5 5.5-14.3 5.5-19.8 0zM88 424h48v36.3l-64.5 11.3-31.1-31.1L51.7 376H88v48z" />
|
||||
</Icon>
|
||||
);
|
||||
};
|
||||
|
||||
Pencil.defaultProps = {
|
||||
size: 16,
|
||||
color: '#000',
|
||||
};
|
||||
|
||||
export default Pencil;
|
||||
export default Sort;
|
||||
|
12
web/src/shared/icons/Sort.tsx
Normal file
12
web/src/shared/icons/Sort.tsx
Normal file
@ -0,0 +1,12 @@
|
||||
import React from 'react';
|
||||
import Icon, { IconProps } from './Icon';
|
||||
|
||||
const Sort: React.FC<IconProps> = ({ width = '16px', height = '16px', className }) => {
|
||||
return (
|
||||
<Icon width={width} height={height} className={className} viewBox="0 0 320 512">
|
||||
<path d="M41 288h238c21.4 0 32.1 25.9 17 41L177 448c-9.4 9.4-24.6 9.4-33.9 0L24 329c-15.1-15.1-4.4-41 17-41zm255-105L177 64c-9.4-9.4-24.6-9.4-33.9 0L24 183c-15.1 15.1-4.4 41 17 41h238c21.4 0 32.1-25.9 17-41z" />
|
||||
</Icon>
|
||||
);
|
||||
};
|
||||
|
||||
export default Sort;
|
@ -1,5 +1,8 @@
|
||||
import Cross from './Cross';
|
||||
import Cog from './Cog';
|
||||
import Sort from './Sort';
|
||||
import Filter from './Filter';
|
||||
import BarChart from './BarChart';
|
||||
import Trash from './Trash';
|
||||
import CheckCircle from './CheckCircle';
|
||||
import Clock from './Clock';
|
||||
@ -58,6 +61,9 @@ export {
|
||||
Clock,
|
||||
CheckSquareOutline,
|
||||
CheckSquare,
|
||||
BarChart,
|
||||
Square,
|
||||
Filter,
|
||||
Sort,
|
||||
ToggleOn,
|
||||
};
|
||||
|
@ -2,6 +2,21 @@
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@apollo/client@^3.0.0-rc.8":
|
||||
version "3.0.0-rc.8"
|
||||
resolved "https://registry.yarnpkg.com/@apollo/client/-/client-3.0.0-rc.8.tgz#70643547ed8bb10d1d96f64320c3510a57f0cf6a"
|
||||
integrity sha512-8Sw4Htao2mZb4vERbvMj9GOf4x/+CLYNuF5gULY7TtMWG4ZANLxbe1DeFCicxVo2xRVQ960nf5vVJhxKcjDOQA==
|
||||
dependencies:
|
||||
"@types/zen-observable" "^0.8.0"
|
||||
"@wry/equality" "^0.1.9"
|
||||
fast-json-stable-stringify "^2.0.0"
|
||||
graphql-tag "^2.10.2"
|
||||
optimism "^0.12.1"
|
||||
symbol-observable "^1.2.0"
|
||||
ts-invariant "^0.4.4"
|
||||
tslib "^1.10.0"
|
||||
zen-observable "^0.8.14"
|
||||
|
||||
"@apollo/federation@0.13.2":
|
||||
version "0.13.2"
|
||||
resolved "https://registry.yarnpkg.com/@apollo/federation/-/federation-0.13.2.tgz#a9f842abd1619fe5cd732c56cfbc45dab0ae784a"
|
||||
@ -3637,6 +3652,13 @@
|
||||
"@types/node" ">=6"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@wry/context@^0.5.2":
|
||||
version "0.5.2"
|
||||
resolved "https://registry.yarnpkg.com/@wry/context/-/context-0.5.2.tgz#f2a5d5ab9227343aa74c81e06533c1ef84598ec7"
|
||||
integrity sha512-B/JLuRZ/vbEKHRUiGj6xiMojST1kHhu4WcreLfNN7q9DqQFrb97cWgf/kiYsPSUCAMVN0HzfFc8XjJdzgZzfjw==
|
||||
dependencies:
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@wry/equality@^0.1.2", "@wry/equality@^0.1.9":
|
||||
version "0.1.9"
|
||||
resolved "https://registry.yarnpkg.com/@wry/equality/-/equality-0.1.9.tgz#b13e18b7a8053c6858aa6c85b54911fb31e3a909"
|
||||
@ -8349,7 +8371,7 @@ graphql-request@^1.5.0:
|
||||
dependencies:
|
||||
cross-fetch "2.2.2"
|
||||
|
||||
graphql-tag@2.10.3, graphql-tag@^2.10.1, graphql-tag@^2.10.3:
|
||||
graphql-tag@2.10.3, graphql-tag@^2.10.1, graphql-tag@^2.10.2, graphql-tag@^2.10.3:
|
||||
version "2.10.3"
|
||||
resolved "https://registry.yarnpkg.com/graphql-tag/-/graphql-tag-2.10.3.tgz#ea1baba5eb8fc6339e4c4cf049dabe522b0edf03"
|
||||
integrity sha512-4FOv3ZKfA4WdOKJeHdz6B3F/vxBLSgmBcGeAFPf4n1F64ltJUvOOerNj0rsJxONQGdhUMynQIvd6LzB+1J5oKA==
|
||||
@ -11668,6 +11690,13 @@ optimism@^0.10.0:
|
||||
dependencies:
|
||||
"@wry/context" "^0.4.0"
|
||||
|
||||
optimism@^0.12.1:
|
||||
version "0.12.1"
|
||||
resolved "https://registry.yarnpkg.com/optimism/-/optimism-0.12.1.tgz#933f9467b9aef0e601655adb9638f893e486ad02"
|
||||
integrity sha512-t8I7HM1dw0SECitBYAqFOVHoBAHEQBTeKjIL9y9ImHzAVkdyPK4ifTgM4VJRDtTUY4r/u5Eqxs4XcGPHaoPkeQ==
|
||||
dependencies:
|
||||
"@wry/context" "^0.5.2"
|
||||
|
||||
optimize-css-assets-webpack-plugin@5.0.3:
|
||||
version "5.0.3"
|
||||
resolved "https://registry.yarnpkg.com/optimize-css-assets-webpack-plugin/-/optimize-css-assets-webpack-plugin-5.0.3.tgz#e2f1d4d94ad8c0af8967ebd7cf138dcb1ef14572"
|
||||
@ -17236,7 +17265,7 @@ zen-observable-ts@^0.8.21:
|
||||
tslib "^1.9.3"
|
||||
zen-observable "^0.8.0"
|
||||
|
||||
zen-observable@^0.8.0:
|
||||
zen-observable@^0.8.0, zen-observable@^0.8.14:
|
||||
version "0.8.15"
|
||||
resolved "https://registry.yarnpkg.com/zen-observable/-/zen-observable-0.8.15.tgz#96415c512d8e3ffd920afd3889604e30b9eaac15"
|
||||
integrity sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ==
|
||||
|
Reference in New Issue
Block a user