diff --git a/.browserslistrc b/.browserslistrc
new file mode 100644
index 00000000..e94f8140
--- /dev/null
+++ b/.browserslistrc
@@ -0,0 +1 @@
+defaults
diff --git a/.eslintrc.js b/.eslintrc.js
new file mode 100644
index 00000000..ab19e415
--- /dev/null
+++ b/.eslintrc.js
@@ -0,0 +1,36 @@
+module.exports = {
+ env: {
+ browser: true,
+ es6: true,
+ jest: true,
+ node: true,
+ },
+ extends: [
+ "eslint:recommended",
+ "plugin:@typescript-eslint/recommended",
+ "prettier/@typescript-eslint",
+ "plugin:react/recommended",
+ "prettier",
+ ],
+ parser: "@typescript-eslint/parser",
+ parserOptions: {
+ ecmaFeatures: {
+ jsx: true,
+ },
+ ecmaVersion: 6,
+ sourceType: "module",
+ },
+ plugins: ["@typescript-eslint", "eslint-plugin-import"],
+ root: true,
+ rules: {
+ "no-console": 1,
+ "sort-imports": 1,
+ "sort-keys": 1,
+ "spaced-comment": ["error", "always", { block: { balanced: true } }],
+ },
+ settings: {
+ react: {
+ version: "detect",
+ },
+ }
+}
diff --git a/.gitignore b/.gitignore
index 931707d1..f4c473f2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -27,3 +27,10 @@
/config/master.key
.env
+
+/public/packs
+/public/packs-test
+/node_modules
+/yarn-error.log
+yarn-debug.log*
+.yarn-integrity
diff --git a/Dockerfile b/Dockerfile
index 9728c2d3..3d62ed5b 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -28,6 +28,10 @@ RUN gem install bundler -v '<2' && \
COPY Gemfile Gemfile.lock .ruby-version ./
RUN bundle install -j4
+# Set up packages, empty cache to save space
+COPY package.json yarn.lock ./
+RUN yarn install --frozen-lockfile --quiet && \
+ yarn cache clean --force
# Create directories for Puma/Nginx & give deploy user access
RUN mkdir -p /shared/pids /shared/sockets && \
diff --git a/Gemfile b/Gemfile
index a6381c79..d0ecc07e 100644
--- a/Gemfile
+++ b/Gemfile
@@ -59,3 +59,5 @@ end
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
+
+gem "webpacker", "~> 5.1"
diff --git a/Gemfile.lock b/Gemfile.lock
index 2fba4bbb..84ffe848 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -165,6 +165,8 @@ GEM
public_suffix (4.0.5)
puma (3.12.6)
rack (2.2.2)
+ rack-proxy (0.6.5)
+ rack
rack-test (1.1.0)
rack (>= 1.0, < 3)
rails (6.0.3.1)
@@ -249,6 +251,7 @@ GEM
sawyer (0.8.2)
addressable (>= 2.3.5)
faraday (> 0.8, < 2.0)
+ semantic_range (2.3.0)
sprockets (3.7.2)
concurrent-ruby (~> 1.0)
rack (> 1, < 3)
@@ -271,6 +274,11 @@ GEM
activemodel (>= 6.0.0)
bindex (>= 0.4.0)
railties (>= 6.0.0)
+ webpacker (5.1.1)
+ activesupport (>= 5.2)
+ rack-proxy (>= 0.6.1)
+ railties (>= 5.2)
+ semantic_range (>= 2.3.0)
websocket-driver (0.7.2)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.4)
@@ -304,6 +312,7 @@ DEPENDENCIES
tzinfo-data
uglifier (>= 1.3.0)
web-console (>= 3.3.0)
+ webpacker (~> 5.1)
RUBY VERSION
ruby 2.6.6p146
diff --git a/Rakefile b/Rakefile
index 517733cc..be1881e6 100644
--- a/Rakefile
+++ b/Rakefile
@@ -10,3 +10,14 @@ namespace :cron do
ComparisonService.refresh_all_comparisons
end
end
+
+if Rails.env.development? || Rails.env.test?
+ desc 'run prettier'
+ task prettier: :environment do
+ system 'yarn prettier'
+ abort 'prettier failed' unless $CHILD_STATUS.exitstatus.zero?
+ end
+
+ Rake::Task[:default].clear
+ task default: %i[prettier spec]
+end
\ No newline at end of file
diff --git a/app/assets/config/manifest.js b/app/assets/config/manifest.js
index b16e53d6..d6463a4c 100644
--- a/app/assets/config/manifest.js
+++ b/app/assets/config/manifest.js
@@ -1,3 +1,4 @@
+/* eslint-disable spaced-comment */
+
//= link_tree ../images
-//= link_directory ../javascripts .js
//= link_directory ../stylesheets .css
diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js
deleted file mode 100644
index 82e6f0f6..00000000
--- a/app/assets/javascripts/application.js
+++ /dev/null
@@ -1,16 +0,0 @@
-// This is a manifest file that'll be compiled into application.js, which will include all the files
-// listed below.
-//
-// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, or any plugin's
-// vendor/assets/javascripts directory can be referenced here using a relative path.
-//
-// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
-// compiled file. JavaScript code in this file should be added after the last require_* statement.
-//
-// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
-// about supported directives.
-//
-//= require rails-ujs
-//= require activestorage
-//= require turbolinks
-//= require_tree .
diff --git a/app/assets/javascripts/channels/.keep b/app/assets/javascripts/channels/.keep
deleted file mode 100644
index e69de29b..00000000
diff --git a/app/assets/javascripts/channels/project.coffee.erb b/app/assets/javascripts/channels/project.coffee.erb
deleted file mode 100644
index 025a69c9..00000000
--- a/app/assets/javascripts/channels/project.coffee.erb
+++ /dev/null
@@ -1,15 +0,0 @@
-$(->
- App.project = App.cable.subscriptions.create {
- channel: "ProjectChannel",
- organization_id: $('#organization_subscription_identifier').val()
- },
- # connected: ->
- # Called when the subscription is ready for use on the server
-
- # disconnected: ->
- # Called when the subscription has been terminated by the server
-
- received: (data) ->
- # Called when there's incoming data on the websocket for this channel
- location.reload() if data.newSnapshots? || data.updatedBlocks?
-)
diff --git a/app/assets/javascripts/organizations.js b/app/assets/javascripts/organizations.js
deleted file mode 100644
index dee720fa..00000000
--- a/app/assets/javascripts/organizations.js
+++ /dev/null
@@ -1,2 +0,0 @@
-// Place all the behaviors and hooks related to the matching controller here.
-// All this logic will automatically be available in application.js.
diff --git a/app/assets/javascripts/projects.js b/app/assets/javascripts/projects.js
deleted file mode 100644
index dee720fa..00000000
--- a/app/assets/javascripts/projects.js
+++ /dev/null
@@ -1,2 +0,0 @@
-// Place all the behaviors and hooks related to the matching controller here.
-// All this logic will automatically be available in application.js.
diff --git a/app/assets/stylesheets/organizations.scss b/app/assets/stylesheets/organizations.scss
deleted file mode 100644
index 39819880..00000000
--- a/app/assets/stylesheets/organizations.scss
+++ /dev/null
@@ -1,3 +0,0 @@
-// Place all the styles related to the organizations controller here.
-// They will automatically be included in application.css.
-// You can use Sass (SCSS) here: http://sass-lang.com/
diff --git a/app/assets/stylesheets/projects.scss b/app/assets/stylesheets/projects.scss
index ac3a9768..79bc6fcf 100644
--- a/app/assets/stylesheets/projects.scss
+++ b/app/assets/stylesheets/projects.scss
@@ -3,6 +3,11 @@
// You can use Sass (SCSS) here: http://sass-lang.com/
.projects_detail {
+ font-family: "Unica77LLWebRegular", "Helvetica Neue", Helvetica, Arial,
+ sans-serif;
+ font-size: 14px;
+ line-height: 1.5;
+
.avatar {
border-radius: 50%;
-moz-border-radius: 50%;
@@ -19,7 +24,6 @@
}
div.project .project_name {
- font-weight: bold;
div.blocked {
display: inline;
}
@@ -78,10 +82,10 @@
}
.projects_dashboard {
- body {
- font-family: "Unica77LLWebRegular";
- font-size: 14px;
- }
+ font-family: "Unica77LLWebRegular", "Helvetica Neue", Helvetica, Arial,
+ sans-serif;
+ font-size: 14px;
+ line-height: 1.5;
h1 {
font-size: 18px;
@@ -90,14 +94,10 @@
line-height: 24px;
clear: both;
margin: 30px;
- font-family: "Unica77LLWebMedium";
- font-weight: normal;
}
h2 {
font-size: 20px;
- font-family: "Unica77LLWebMedium";
- font-weight: normal;
color: black;
}
@@ -135,9 +135,10 @@
}
.todo .project.aged {
- background-image: image-url("aged_top_left.png"), image-url("aged_top_right.png");
- box-shadow: inset 0 0 40px hsla(33,42%,66%,.6);
- background-position: 0 0,100% 0;
+ background-image: image-url("aged_top_left.png"),
+ image-url("aged_top_right.png");
+ box-shadow: inset 0 0 40px hsla(33, 42%, 66%, 0.6);
+ background-position: 0 0, 100% 0;
background-repeat: no-repeat;
}
@@ -171,8 +172,8 @@
color: #900;
}
.todo .project .stage {
- font-family: "Unica77LLWebMedium";
font-size: 18px;
+ font-weight: bold;
}
.todo .project .who {
diff --git a/app/assets/javascripts/cable.js b/app/javascript/channels/consumer.ts
similarity index 50%
rename from app/assets/javascripts/cable.js
rename to app/javascript/channels/consumer.ts
index 739aa5f0..0eceb59b 100644
--- a/app/assets/javascripts/cable.js
+++ b/app/javascript/channels/consumer.ts
@@ -1,13 +1,6 @@
// Action Cable provides the framework to deal with WebSockets in Rails.
// You can generate new channels where WebSocket features live using the `rails generate channel` command.
-//
-//= require action_cable
-//= require_self
-//= require_tree ./channels
-(function() {
- this.App || (this.App = {});
+import { createConsumer } from "@rails/actioncable"
- App.cable = ActionCable.createConsumer();
-
-}).call(this);
+export default createConsumer()
diff --git a/app/javascript/channels/index.ts b/app/javascript/channels/index.ts
new file mode 100644
index 00000000..c515e606
--- /dev/null
+++ b/app/javascript/channels/index.ts
@@ -0,0 +1,5 @@
+// Load all the channels within this directory and all subdirectories.
+// Channel files must be named *_channel.ts.
+
+const channels = require.context(".", true, /_channel\.ts$/)
+channels.keys().forEach(channels)
diff --git a/app/javascript/channels/project_channel.ts b/app/javascript/channels/project_channel.ts
new file mode 100644
index 00000000..f176062a
--- /dev/null
+++ b/app/javascript/channels/project_channel.ts
@@ -0,0 +1,23 @@
+import $ from "jquery"
+import consumer from "./consumer"
+
+consumer.subscriptions.create(
+ {
+ channel: "ProjectChannel",
+ organization_id: $("#organization_subscription_identifier").val(),
+ },
+ {
+ connected() {
+ // Called when the subscription is ready for use on the server
+ },
+ disconnected() {
+ // Called when the subscription has been terminated by the server
+ },
+ received(data) {
+ // Called when there's incoming data on the websocket for this channel
+ if (data.newSnapshots || data.updatedBlocks) {
+ location.reload()
+ }
+ },
+ },
+)
diff --git a/app/javascript/components/MainLayout.tsx b/app/javascript/components/MainLayout.tsx
new file mode 100644
index 00000000..38e8ac3a
--- /dev/null
+++ b/app/javascript/components/MainLayout.tsx
@@ -0,0 +1,26 @@
+import { ArtsyMarkIcon, Box, Flex, Link, Sans, Theme } from "@artsy/palette"
+import React from "react"
+
+export const MainLayout: React.FC = () => {
+ return (
+