Skip to content

Commit e217980

Browse files
New design! (#103)
* New design! * Clean up * Remove invalid test
1 parent f81e4d5 commit e217980

24 files changed

Lines changed: 1155 additions & 275 deletions

Gemfile.lock

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,7 @@ GEM
250250

251251
PLATFORMS
252252
arm64-darwin-23
253+
arm64-darwin-24
253254
x86_64-darwin-22
254255
x86_64-linux
255256

Lines changed: 82 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,109 @@
1+
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap');
2+
13
@tailwind base;
24
@tailwind components;
35
@tailwind utilities;
46

57
@layer base {
8+
body {
9+
@apply antialiased;
10+
}
11+
612
h1 {
7-
@apply text-2xl font-bold;
13+
@apply text-4xl md:text-5xl font-bold tracking-tight text-charcoal;
814
}
15+
916
h2 {
10-
@apply text-xl font-semibold;
17+
@apply text-2xl md:text-3xl font-semibold tracking-tight text-charcoal;
1118
}
19+
1220
h3 {
13-
@apply text-lg font-medium;
21+
@apply text-xl md:text-2xl font-medium text-charcoal;
22+
}
23+
24+
p {
25+
@apply text-gray-700 leading-relaxed;
26+
}
27+
28+
a {
29+
@apply transition-colors duration-200;
30+
}
31+
}
32+
33+
@layer components {
34+
.btn-primary {
35+
@apply inline-flex items-center px-6 py-3 border border-transparent text-base font-medium rounded-full shadow-soft text-white bg-ruby-500 hover:bg-ruby-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-ruby-500 transition-all duration-200 hover:shadow-lg hover:-translate-y-0.5;
36+
}
37+
38+
.btn-secondary {
39+
@apply inline-flex items-center px-6 py-3 border border-ruby-200 text-base font-medium rounded-full text-ruby-600 bg-white hover:bg-ruby-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-ruby-500 transition-all duration-200 hover:shadow-md;
40+
}
41+
42+
.card {
43+
@apply bg-white rounded-2xl shadow-soft hover:shadow-xl transition-all duration-300 overflow-hidden;
44+
}
45+
46+
.gradient-text {
47+
@apply bg-gradient-to-r from-ruby-500 to-ruby-700 bg-clip-text text-transparent;
1448
}
1549
}
1650

1751
.coc a {
18-
@apply text-ruby underline;
52+
@apply text-ruby-500 underline hover:text-ruby-600;
1953
}
2054

2155
.coc p {
22-
@apply mb-2;
56+
@apply mb-4;
57+
}
58+
59+
a.external-link {
60+
@apply inline-flex items-center gap-1 text-ruby-500 hover:text-ruby-600;
2361
}
2462

2563
a.external-link::after {
2664
content: '';
2765
display: inline-block;
2866
width: 1em;
2967
height: 1em;
30-
margin-left: 0.25em;
3168
background-size: 1em;
3269
background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0iI2Q2NDA0NSIgY2xhc3M9InNpemUtNCI+CiAgPHBhdGggZD0iTTYuMjIgOC43MmEuNzUuNzUgMCAwIDAgMS4wNiAxLjA2bDUuMjItNS4yMnYxLjY5YS43NS43NSAwIDAgMCAxLjUgMHYtMy41YS43NS43NSAwIDAgMC0uNzUtLjc1aC0zLjVhLjc1Ljc1IDAgMCAwIDAgMS41aDEuNjlMNi4yMiA4LjcyWiIgLz4KICA8cGF0aCBkPSJNMy41IDYuNzVjMC0uNjkuNTYtMS4yNSAxLjI1LTEuMjVIN0EuNzUuNzUgMCAwIDAgNyA0SDQuNzVBMi43NSAyLjc1IDAgMCAwIDIgNi43NXY0LjVBMi43NSAyLjc1IDAgMCAwIDQuNzUgMTRoNC41QTIuNzUgMi43NSAwIDAgMCAxMiAxMS4yNVY5YS43NS43NSAwIDAgMC0xLjUgMHYyLjI1YzAgLjY5LS41NiAxLjI1LTEuMjUgMS4yNWgtNC41Yy0uNjkgMC0xLjI1LS41Ni0xLjI1LTEuMjV2LTQuNVoiIC8+Cjwvc3ZnPgo=");
70+
transition: transform 0.2s ease;
71+
}
72+
73+
a.external-link:hover::after {
74+
transform: translate(2px, -2px);
75+
}
76+
77+
/* Smooth scroll behavior */
78+
html {
79+
scroll-behavior: smooth;
80+
}
81+
82+
/* Custom animations */
83+
.animate-in {
84+
animation: fade-in 0.5s ease-in-out;
85+
}
86+
87+
.animate-up {
88+
animation: slide-up 0.5s ease-out;
89+
}
90+
91+
/* ConvertKit form input overrides for better visibility */
92+
.formkit-form[data-uid="4c3ae9b5e4"] .formkit-input {
93+
@apply px-4 py-3 border border-gray-300 rounded-lg shadow-sm transition-colors;
94+
border-color: rgb(209, 213, 219) !important;
95+
}
96+
97+
.formkit-form[data-uid="4c3ae9b5e4"] .formkit-input:focus {
98+
@apply ring-1 ring-ruby-500 border-ruby-500;
99+
border-color: rgb(214, 64, 69) !important;
100+
}
101+
102+
.formkit-form[data-uid="4c3ae9b5e4"] .formkit-submit {
103+
@apply px-6 py-3 bg-ruby-500 hover:bg-ruby-600 rounded-full transition-all duration-200 hover:shadow-lg hover:-translate-y-0.5;
104+
background-color: rgb(214, 64, 69) !important;
105+
}
106+
107+
.formkit-form[data-uid="4c3ae9b5e4"] .formkit-submit:hover {
108+
background-color: rgb(185, 28, 28) !important;
33109
}

app/controllers/events_controller.rb

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,8 @@ def all
1414
end
1515
end
1616

17-
def show
18-
@event = Event.with_all_rich_text.find_by!(slug: params[:slug])
19-
rescue ActiveRecord::RecordNotFound
20-
redirect_to all_events_path, error: 'Event not found'
21-
end
22-
2317
def past
2418
@events = Event.with_all_rich_text.past
25-
render :index
2619
end
2720

2821
def next
Lines changed: 29 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
import { Controller } from "@hotwired/stimulus"
22

3+
// Connects to data-controller="clipboard"
34
export default class extends Controller {
4-
static targets = ["button", "source"]
5+
static targets = ["source", "button", "default", "success"]
56

67
static values = {
7-
successContent: String,
8-
successDuration: {
9-
type: Number,
10-
default: 2000,
11-
}
8+
successContent: { type: String, default: "✅" },
9+
successDuration: { type: Number, default: 2000 }
1210
}
1311

1412
connect() {
@@ -19,23 +17,32 @@ export default class extends Controller {
1917

2018
copy(event) {
2119
event.preventDefault()
22-
23-
const text = this.sourceTarget.innerHTML || this.sourceTarget.value
24-
25-
navigator.clipboard.writeText(text).then(() => this.copied())
20+
21+
const text = this.sourceTarget.value || this.sourceTarget.innerHTML
22+
navigator.clipboard.writeText(text).then(() => {
23+
// Show success state
24+
if (this.hasDefaultTarget && this.hasSuccessTarget) {
25+
this.defaultTarget.classList.add('hidden')
26+
this.successTarget.classList.remove('hidden')
27+
28+
// Reset after specified duration
29+
setTimeout(() => {
30+
this.defaultTarget.classList.remove('hidden')
31+
this.successTarget.classList.add('hidden')
32+
}, this.successDurationValue)
33+
} else if (this.hasButtonTarget) {
34+
// Fallback for old implementation
35+
const originalContent = this.buttonTarget.innerHTML
36+
this.buttonTarget.innerHTML = this.successContentValue
37+
38+
setTimeout(() => {
39+
this.buttonTarget.innerHTML = originalContent
40+
}, this.successDurationValue)
41+
}
42+
})
2643
}
2744

28-
copied() {
29-
if (!this.hasButtonTarget) return
30-
31-
if (this.timeout) {
32-
clearTimeout(this.timeout)
33-
}
34-
35-
this.buttonTarget.innerHTML = this.successContentValue
36-
37-
this.timeout = setTimeout(() => {
38-
this.buttonTarget.innerHTML = this.originalContent
39-
}, this.successDurationValue)
45+
get successContentValue() {
46+
return this.data.get("successContentValue")
4047
}
4148
}
Lines changed: 129 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,136 @@
11
<%= form_with(model: [:admin, event]) do |form| %>
22
<% if event.errors.any? %>
3-
<div style="color: red">
4-
<h2><%= pluralize(event.errors.count, "error") %>
5-
prohibited this event from being saved:</h2>
6-
7-
<ul>
8-
<% event.errors.each do |error| %>
9-
<li><%= error.full_message %></li>
10-
<% end %>
11-
</ul>
3+
<div class="rounded-lg bg-red-50 p-4 mb-6">
4+
<div class="flex">
5+
<div class="flex-shrink-0">
6+
<svg class="h-5 w-5 text-red-400" viewBox="0 0 20 20" fill="currentColor">
7+
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd" />
8+
</svg>
9+
</div>
10+
<div class="ml-3">
11+
<h3 class="text-sm font-medium text-red-800">
12+
<%= pluralize(event.errors.count, "error") %> prohibited this event from being saved:
13+
</h3>
14+
<div class="mt-2 text-sm text-red-700">
15+
<ul class="list-disc list-inside space-y-1">
16+
<% event.errors.each do |error| %>
17+
<li><%= error.full_message %></li>
18+
<% end %>
19+
</ul>
20+
</div>
21+
</div>
22+
</div>
1223
</div>
1324
<% end %>
1425

15-
<div class='flex flex-col'>
16-
<%= form.label :name %>
17-
<%= form.text_field :name, required: true %>
18-
<%= form.label :location %>
19-
<%= form.rich_text_area :location, required: true %>
20-
<%= form.label :rsvp_link %>
21-
<%= form.text_field :rsvp_link, required: true %>
22-
<%= form.label :description %>
23-
<%= form.rich_text_area :description %>
24-
<%= form.label :sponsor %>
25-
<%= form.text_field :sponsor %>
26-
<%= form.label :sponsor_link %>
27-
<%= form.text_field :sponsor_link %>
28-
<%= form.label :sponsor_logo %>
29-
<%= form.text_field :sponsor_logo %>
30-
<%= form.label :start_at %>
31-
<%= form.datetime_field :start_at,
32-
include_seconds: false,
33-
value:
34-
event
35-
&.start_at
36-
&.in_time_zone("Eastern Time (US & Canada)")
37-
.presence ||
38-
Time.zone.now.in_time_zone("Eastern Time (US & Canada)") %>
39-
<%= form.label :status %>
40-
<%= form.select :status, options_for_select(Event.statuses_for_select, event.status) %>
41-
<%= form.submit %>
26+
<div class="space-y-6">
27+
<!-- Basic Information -->
28+
<div class="card p-6">
29+
<h3 class="text-lg font-semibold mb-4">Basic Information</h3>
30+
31+
<div class="space-y-4">
32+
<div>
33+
<%= form.label :name, class: "block text-sm font-medium text-gray-700 mb-1" %>
34+
<%= form.text_field :name, required: true, class: "block w-full px-4 py-3 rounded-lg border border-gray-300 shadow-sm focus:border-ruby-500 focus:ring-1 focus:ring-ruby-500 transition-colors" %>
35+
</div>
36+
37+
<div>
38+
<%= form.label :start_at, class: "block text-sm font-medium text-gray-700 mb-1" %>
39+
<%= form.datetime_field :start_at,
40+
include_seconds: false,
41+
value: event&.start_at&.in_time_zone("Eastern Time (US & Canada)").presence || Time.zone.now.in_time_zone("Eastern Time (US & Canada)"),
42+
class: "block w-full px-4 py-3 rounded-lg border border-gray-300 shadow-sm focus:border-ruby-500 focus:ring-1 focus:ring-ruby-500 transition-colors" %>
43+
<p class="mt-1 text-sm text-gray-500">Eastern Time (US & Canada)</p>
44+
</div>
45+
46+
<div>
47+
<%= form.label :status, class: "block text-sm font-medium text-gray-700 mb-1" %>
48+
<%= form.select :status,
49+
options_for_select(Event.statuses_for_select, event.status),
50+
{},
51+
class: "block w-full px-4 py-3 rounded-lg border border-gray-300 shadow-sm focus:border-ruby-500 focus:ring-1 focus:ring-ruby-500 transition-colors" %>
52+
</div>
53+
54+
<div>
55+
<%= form.label :rsvp_link, "RSVP Link", class: "block text-sm font-medium text-gray-700 mb-1" %>
56+
<%= form.text_field :rsvp_link, required: true, placeholder: "https://...", class: "block w-full px-4 py-3 rounded-lg border border-gray-300 shadow-sm focus:border-ruby-500 focus:ring-1 focus:ring-ruby-500 transition-colors placeholder-gray-400" %>
57+
</div>
58+
</div>
59+
</div>
60+
61+
<!-- Content -->
62+
<div class="card p-6">
63+
<h3 class="text-lg font-semibold mb-4">Event Details</h3>
64+
65+
<div class="space-y-4">
66+
<div>
67+
<%= form.label :location, class: "block text-sm font-medium text-gray-700 mb-1" %>
68+
<div class="prose-editor">
69+
<%= form.rich_text_area :location, required: true, class: "block w-full" %>
70+
</div>
71+
</div>
72+
73+
<div>
74+
<%= form.label :description, "Agenda", class: "block text-sm font-medium text-gray-700 mb-1" %>
75+
<div class="prose-editor">
76+
<%= form.rich_text_area :description, class: "block w-full" %>
77+
</div>
78+
</div>
79+
</div>
80+
</div>
81+
82+
<!-- Sponsor Information -->
83+
<div class="card p-6">
84+
<h3 class="text-lg font-semibold mb-4">Sponsor Information</h3>
85+
<p class="text-sm text-gray-600 mb-4">Optional: Add sponsor details if this event has a sponsor</p>
86+
87+
<div class="space-y-4">
88+
<div>
89+
<%= form.label :sponsor, "Sponsor Name", class: "block text-sm font-medium text-gray-700 mb-1" %>
90+
<%= form.text_field :sponsor, class: "block w-full px-4 py-3 rounded-lg border border-gray-300 shadow-sm focus:border-ruby-500 focus:ring-1 focus:ring-ruby-500 transition-colors" %>
91+
</div>
92+
93+
<div>
94+
<%= form.label :sponsor_link, "Sponsor Website", class: "block text-sm font-medium text-gray-700 mb-1" %>
95+
<%= form.text_field :sponsor_link, placeholder: "https://...", class: "block w-full px-4 py-3 rounded-lg border border-gray-300 shadow-sm focus:border-ruby-500 focus:ring-1 focus:ring-ruby-500 transition-colors placeholder-gray-400" %>
96+
</div>
97+
98+
<div>
99+
<%= form.label :sponsor_logo, "Logo Filename", class: "block text-sm font-medium text-gray-700 mb-1" %>
100+
<%= form.text_field :sponsor_logo, placeholder: "company.png", class: "block w-full px-4 py-3 rounded-lg border border-gray-300 shadow-sm focus:border-ruby-500 focus:ring-1 focus:ring-ruby-500 transition-colors placeholder-gray-400" %>
101+
<p class="mt-1 text-sm text-gray-500">Logo should be placed in app/assets/images/sponsors/</p>
102+
</div>
103+
</div>
104+
</div>
105+
106+
<!-- Form Actions -->
107+
<div class="flex items-center justify-end gap-4 pt-6">
108+
<%= link_to "Cancel", admin_events_path, class: "btn-secondary" %>
109+
<%= form.submit class: "btn-primary" %>
110+
</div>
42111
</div>
43112
<% end %>
113+
114+
<style>
115+
/* Rich text editor styling */
116+
.prose-editor {
117+
@apply rounded-lg border border-gray-300 overflow-hidden shadow-sm;
118+
}
119+
120+
.prose-editor:focus-within {
121+
@apply ring-1 ring-ruby-500 border-ruby-500;
122+
}
123+
124+
.prose-editor trix-editor {
125+
@apply min-h-[150px] px-4 py-3;
126+
}
127+
128+
.prose-editor trix-toolbar {
129+
@apply border-b border-gray-200 px-2;
130+
}
131+
132+
/* Ensure trix editor has consistent padding */
133+
.prose-editor trix-editor:empty:not(:focus)::before {
134+
@apply text-gray-400;
135+
}
136+
</style>

0 commit comments

Comments
 (0)