not_pressed-core 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE.txt +41 -0
- data/README.md +285 -0
- data/app/assets/javascripts/not_pressed/lightbox.js +110 -0
- data/app/assets/stylesheets/not_pressed/admin.css +1161 -0
- data/app/assets/stylesheets/not_pressed/content.css +193 -0
- data/app/assets/stylesheets/not_pressed/gallery.css +117 -0
- data/app/assets/stylesheets/not_pressed/themes/starter.css +118 -0
- data/app/controllers/not_pressed/admin/base_controller.rb +21 -0
- data/app/controllers/not_pressed/admin/categories_controller.rb +53 -0
- data/app/controllers/not_pressed/admin/content_blocks_controller.rb +73 -0
- data/app/controllers/not_pressed/admin/dashboard_controller.rb +19 -0
- data/app/controllers/not_pressed/admin/forms_controller.rb +86 -0
- data/app/controllers/not_pressed/admin/media_attachments_controller.rb +94 -0
- data/app/controllers/not_pressed/admin/pages_controller.rb +122 -0
- data/app/controllers/not_pressed/admin/plugins_controller.rb +121 -0
- data/app/controllers/not_pressed/admin/settings_controller.rb +19 -0
- data/app/controllers/not_pressed/admin/tags_controller.rb +37 -0
- data/app/controllers/not_pressed/admin/themes_controller.rb +104 -0
- data/app/controllers/not_pressed/application_controller.rb +6 -0
- data/app/controllers/not_pressed/blog_controller.rb +83 -0
- data/app/controllers/not_pressed/form_submissions_controller.rb +70 -0
- data/app/controllers/not_pressed/pages_controller.rb +36 -0
- data/app/controllers/not_pressed/robots_controller.rb +34 -0
- data/app/controllers/not_pressed/sitemaps_controller.rb +12 -0
- data/app/helpers/not_pressed/admin_helper.rb +41 -0
- data/app/helpers/not_pressed/application_helper.rb +6 -0
- data/app/helpers/not_pressed/code_injection_helper.rb +29 -0
- data/app/helpers/not_pressed/content_helper.rb +13 -0
- data/app/helpers/not_pressed/form_helper.rb +80 -0
- data/app/helpers/not_pressed/media_helper.rb +28 -0
- data/app/helpers/not_pressed/seo_helper.rb +69 -0
- data/app/helpers/not_pressed/theme_helper.rb +42 -0
- data/app/mailers/not_pressed/application_mailer.rb +10 -0
- data/app/mailers/not_pressed/form_mailer.rb +15 -0
- data/app/models/concerns/not_pressed/sluggable.rb +43 -0
- data/app/models/not_pressed/category.rb +16 -0
- data/app/models/not_pressed/content_block.rb +46 -0
- data/app/models/not_pressed/form.rb +55 -0
- data/app/models/not_pressed/form_field.rb +23 -0
- data/app/models/not_pressed/form_submission.rb +19 -0
- data/app/models/not_pressed/media_attachment.rb +68 -0
- data/app/models/not_pressed/page.rb +182 -0
- data/app/models/not_pressed/page_version.rb +15 -0
- data/app/models/not_pressed/setting.rb +20 -0
- data/app/models/not_pressed/tag.rb +15 -0
- data/app/models/not_pressed/tagging.rb +12 -0
- data/app/plugins/not_pressed/analytics_plugin.rb +106 -0
- data/app/plugins/not_pressed/callout_block_plugin.rb +43 -0
- data/app/themes/not_pressed/starter_theme.rb +26 -0
- data/app/views/layouts/not_pressed/admin.html.erb +745 -0
- data/app/views/layouts/not_pressed/application.html.erb +12 -0
- data/app/views/layouts/not_pressed/page.html.erb +22 -0
- data/app/views/not_pressed/admin/categories/index.html.erb +58 -0
- data/app/views/not_pressed/admin/content_blocks/_block.html.erb +18 -0
- data/app/views/not_pressed/admin/content_blocks/_block_picker.html.erb +32 -0
- data/app/views/not_pressed/admin/content_blocks/create.turbo_stream.erb +3 -0
- data/app/views/not_pressed/admin/content_blocks/destroy.turbo_stream.erb +1 -0
- data/app/views/not_pressed/admin/content_blocks/editors/_callout.html.erb +38 -0
- data/app/views/not_pressed/admin/content_blocks/editors/_code.html.erb +26 -0
- data/app/views/not_pressed/admin/content_blocks/editors/_divider.html.erb +4 -0
- data/app/views/not_pressed/admin/content_blocks/editors/_form.html.erb +16 -0
- data/app/views/not_pressed/admin/content_blocks/editors/_gallery.html.erb +75 -0
- data/app/views/not_pressed/admin/content_blocks/editors/_heading.html.erb +26 -0
- data/app/views/not_pressed/admin/content_blocks/editors/_html.html.erb +13 -0
- data/app/views/not_pressed/admin/content_blocks/editors/_image.html.erb +56 -0
- data/app/views/not_pressed/admin/content_blocks/editors/_quote.html.erb +24 -0
- data/app/views/not_pressed/admin/content_blocks/editors/_text.html.erb +28 -0
- data/app/views/not_pressed/admin/content_blocks/editors/_video.html.erb +25 -0
- data/app/views/not_pressed/admin/dashboard/index.html.erb +60 -0
- data/app/views/not_pressed/admin/forms/_field_row.html.erb +33 -0
- data/app/views/not_pressed/admin/forms/_form.html.erb +75 -0
- data/app/views/not_pressed/admin/forms/edit.html.erb +1 -0
- data/app/views/not_pressed/admin/forms/index.html.erb +32 -0
- data/app/views/not_pressed/admin/forms/new.html.erb +1 -0
- data/app/views/not_pressed/admin/forms/submissions.html.erb +34 -0
- data/app/views/not_pressed/admin/media_attachments/_media_card.html.erb +21 -0
- data/app/views/not_pressed/admin/media_attachments/_picker.html.erb +19 -0
- data/app/views/not_pressed/admin/media_attachments/edit.html.erb +57 -0
- data/app/views/not_pressed/admin/media_attachments/index.html.erb +48 -0
- data/app/views/not_pressed/admin/pages/_form.html.erb +177 -0
- data/app/views/not_pressed/admin/pages/_page_tree_node.html.erb +32 -0
- data/app/views/not_pressed/admin/pages/edit.html.erb +69 -0
- data/app/views/not_pressed/admin/pages/index.html.erb +21 -0
- data/app/views/not_pressed/admin/pages/new.html.erb +1 -0
- data/app/views/not_pressed/admin/pages/preview.html.erb +17 -0
- data/app/views/not_pressed/admin/plugins/_settings_form.html.erb +59 -0
- data/app/views/not_pressed/admin/plugins/index.html.erb +48 -0
- data/app/views/not_pressed/admin/plugins/show.html.erb +54 -0
- data/app/views/not_pressed/admin/settings/code_injection.html.erb +21 -0
- data/app/views/not_pressed/admin/shared/_breadcrumbs.html.erb +14 -0
- data/app/views/not_pressed/admin/shared/_flash.html.erb +7 -0
- data/app/views/not_pressed/admin/shared/_modal.html.erb +9 -0
- data/app/views/not_pressed/admin/shared/_sidebar.html.erb +18 -0
- data/app/views/not_pressed/admin/tags/index.html.erb +52 -0
- data/app/views/not_pressed/admin/themes/index.html.erb +60 -0
- data/app/views/not_pressed/admin/themes/show.html.erb +66 -0
- data/app/views/not_pressed/blog/_post_card.html.erb +24 -0
- data/app/views/not_pressed/blog/feed.rss.builder +22 -0
- data/app/views/not_pressed/blog/index.html.erb +56 -0
- data/app/views/not_pressed/blog/show.html.erb +41 -0
- data/app/views/not_pressed/form_mailer/submission_notification.text.erb +8 -0
- data/app/views/not_pressed/pages/show.html.erb +4 -0
- data/app/views/themes/starter/layouts/not_pressed/default.html.erb +36 -0
- data/app/views/themes/starter/layouts/not_pressed/full_width.html.erb +36 -0
- data/app/views/themes/starter/layouts/not_pressed/sidebar.html.erb +41 -0
- data/config/routes.rb +81 -0
- data/db/migrate/20260310000001_create_not_pressed_pages.rb +20 -0
- data/db/migrate/20260310000002_create_not_pressed_content_blocks.rb +17 -0
- data/db/migrate/20260310000003_create_not_pressed_media_attachments.rb +14 -0
- data/db/migrate/20260310000004_add_content_type_to_not_pressed_pages.rb +8 -0
- data/db/migrate/20260310000005_add_publishing_fields_to_not_pressed_pages.rb +8 -0
- data/db/migrate/20260310000006_create_not_pressed_page_versions.rb +16 -0
- data/db/migrate/20260310000007_add_settings_fields_to_not_pressed_pages.rb +11 -0
- data/db/migrate/20260311000001_create_not_pressed_forms.rb +42 -0
- data/db/migrate/20260311000002_add_seo_fields_to_not_pressed_pages.rb +10 -0
- data/db/migrate/20260311000003_add_code_injection_to_not_pressed_pages.rb +8 -0
- data/db/migrate/20260311000004_create_not_pressed_settings.rb +14 -0
- data/db/migrate/20260312000001_create_not_pressed_categories.rb +16 -0
- data/db/migrate/20260312000002_create_not_pressed_tags.rb +15 -0
- data/db/migrate/20260312000003_create_not_pressed_taggings.rb +14 -0
- data/db/migrate/20260312000004_add_category_id_to_not_pressed_pages.rb +7 -0
- data/lib/generators/not_pressed/install/install_generator.rb +52 -0
- data/lib/generators/not_pressed/install/templates/initializer.rb.tt +89 -0
- data/lib/generators/not_pressed/install/templates/seeds.rb.tt +131 -0
- data/lib/generators/not_pressed/plugin/plugin_generator.rb +37 -0
- data/lib/generators/not_pressed/plugin/templates/plugin.rb.tt +23 -0
- data/lib/not_pressed/admin/authentication.rb +48 -0
- data/lib/not_pressed/admin/menu_registry.rb +100 -0
- data/lib/not_pressed/configuration.rb +77 -0
- data/lib/not_pressed/content_type.rb +23 -0
- data/lib/not_pressed/content_type_builder.rb +51 -0
- data/lib/not_pressed/content_type_registry.rb +45 -0
- data/lib/not_pressed/engine.rb +132 -0
- data/lib/not_pressed/hooks.rb +166 -0
- data/lib/not_pressed/navigation/builder.rb +148 -0
- data/lib/not_pressed/navigation/menu.rb +54 -0
- data/lib/not_pressed/navigation/menu_item.rb +33 -0
- data/lib/not_pressed/navigation/node.rb +45 -0
- data/lib/not_pressed/navigation/partial_parser.rb +96 -0
- data/lib/not_pressed/navigation/route_inspector.rb +98 -0
- data/lib/not_pressed/navigation.rb +6 -0
- data/lib/not_pressed/plugin.rb +354 -0
- data/lib/not_pressed/plugin_importer.rb +133 -0
- data/lib/not_pressed/plugin_manager.rb +196 -0
- data/lib/not_pressed/plugin_packager.rb +129 -0
- data/lib/not_pressed/rendering/block_renderer.rb +222 -0
- data/lib/not_pressed/rendering/renderer_registry.rb +154 -0
- data/lib/not_pressed/rendering.rb +8 -0
- data/lib/not_pressed/seo/sitemap_builder.rb +61 -0
- data/lib/not_pressed/theme.rb +191 -0
- data/lib/not_pressed/theme_importer.rb +133 -0
- data/lib/not_pressed/theme_packager.rb +180 -0
- data/lib/not_pressed/theme_registry.rb +123 -0
- data/lib/not_pressed/version.rb +5 -0
- data/lib/not_pressed.rb +65 -0
- metadata +258 -0
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/* NotPressed Content Styles
|
|
2
|
+
* Styles for rendered block content on public pages and preview.
|
|
3
|
+
* Uses CSS custom properties for theme overrides.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
:root {
|
|
7
|
+
/* Colors */
|
|
8
|
+
--np-color-primary: #4f46e5;
|
|
9
|
+
--np-color-text: #1e293b;
|
|
10
|
+
--np-color-heading: #0f172a;
|
|
11
|
+
--np-color-background: #fff;
|
|
12
|
+
--np-color-muted: #64748b;
|
|
13
|
+
--np-color-quote-text: #334155;
|
|
14
|
+
--np-color-quote-bg: #f8fafc;
|
|
15
|
+
--np-color-border: #e2e8f0;
|
|
16
|
+
--np-color-code-bg: #1e1e2e;
|
|
17
|
+
--np-color-code-text: #cdd6f4;
|
|
18
|
+
|
|
19
|
+
/* Fonts */
|
|
20
|
+
--np-font-body: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
21
|
+
--np-font-code: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
|
|
22
|
+
|
|
23
|
+
/* Layout */
|
|
24
|
+
--np-max-width: 48rem;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/* Page layout */
|
|
28
|
+
.np-page-body {
|
|
29
|
+
margin: 0;
|
|
30
|
+
font-family: var(--np-font-body);
|
|
31
|
+
color: var(--np-color-text);
|
|
32
|
+
background-color: var(--np-color-background);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.np-page-main {
|
|
36
|
+
max-width: var(--np-max-width);
|
|
37
|
+
margin: 0 auto;
|
|
38
|
+
padding: 2rem 1.5rem;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.np-page-article h1 {
|
|
42
|
+
font-size: 2.25rem;
|
|
43
|
+
font-weight: 700;
|
|
44
|
+
line-height: 1.2;
|
|
45
|
+
margin: 0 0 2rem;
|
|
46
|
+
color: var(--np-color-heading);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.np-preview-body {
|
|
50
|
+
margin: 0;
|
|
51
|
+
font-family: var(--np-font-body);
|
|
52
|
+
color: var(--np-color-text);
|
|
53
|
+
background-color: var(--np-color-background);
|
|
54
|
+
padding: 1.5rem;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.np-preview-article h1 {
|
|
58
|
+
font-size: 2rem;
|
|
59
|
+
font-weight: 700;
|
|
60
|
+
line-height: 1.2;
|
|
61
|
+
margin: 0 0 1.5rem;
|
|
62
|
+
color: var(--np-color-heading);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/* Text blocks */
|
|
66
|
+
.np-content-text {
|
|
67
|
+
line-height: 1.75;
|
|
68
|
+
max-width: 65ch;
|
|
69
|
+
margin-bottom: 1.5rem;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.np-content-text p {
|
|
73
|
+
margin: 0 0 1rem;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.np-content-text a {
|
|
77
|
+
color: var(--np-color-primary);
|
|
78
|
+
text-decoration: underline;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.np-content-text ul,
|
|
82
|
+
.np-content-text ol {
|
|
83
|
+
padding-left: 1.5rem;
|
|
84
|
+
margin-bottom: 1rem;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/* Heading blocks */
|
|
88
|
+
.np-content-heading {
|
|
89
|
+
font-weight: 700;
|
|
90
|
+
line-height: 1.3;
|
|
91
|
+
margin: 2rem 0 1rem;
|
|
92
|
+
color: var(--np-color-heading);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
h2.np-content-heading { font-size: 1.75rem; }
|
|
96
|
+
h3.np-content-heading { font-size: 1.5rem; }
|
|
97
|
+
h4.np-content-heading { font-size: 1.25rem; }
|
|
98
|
+
h5.np-content-heading { font-size: 1.125rem; }
|
|
99
|
+
h6.np-content-heading { font-size: 1rem; }
|
|
100
|
+
|
|
101
|
+
/* Image blocks */
|
|
102
|
+
.np-content-image {
|
|
103
|
+
margin: 1.5rem 0;
|
|
104
|
+
text-align: center;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.np-content-image img {
|
|
108
|
+
max-width: 100%;
|
|
109
|
+
height: auto;
|
|
110
|
+
border-radius: 0.375rem;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.np-content-image figcaption {
|
|
114
|
+
font-style: italic;
|
|
115
|
+
font-size: 0.875rem;
|
|
116
|
+
color: var(--np-color-muted);
|
|
117
|
+
margin-top: 0.5rem;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/* Video blocks */
|
|
121
|
+
.np-content-video {
|
|
122
|
+
margin: 1.5rem 0;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.np-content-video iframe,
|
|
126
|
+
.np-content-video video {
|
|
127
|
+
max-width: 100%;
|
|
128
|
+
width: 100%;
|
|
129
|
+
aspect-ratio: 16 / 9;
|
|
130
|
+
border-radius: 0.375rem;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.np-content-video figcaption {
|
|
134
|
+
font-style: italic;
|
|
135
|
+
font-size: 0.875rem;
|
|
136
|
+
color: var(--np-color-muted);
|
|
137
|
+
margin-top: 0.5rem;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/* Code blocks */
|
|
141
|
+
.np-content-code {
|
|
142
|
+
background-color: var(--np-color-code-bg);
|
|
143
|
+
color: var(--np-color-code-text);
|
|
144
|
+
padding: 1.25rem;
|
|
145
|
+
border-radius: 0.5rem;
|
|
146
|
+
overflow-x: auto;
|
|
147
|
+
font-family: var(--np-font-code);
|
|
148
|
+
font-size: 0.875rem;
|
|
149
|
+
line-height: 1.6;
|
|
150
|
+
margin: 1.5rem 0;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.np-content-code code {
|
|
154
|
+
background: none;
|
|
155
|
+
padding: 0;
|
|
156
|
+
font-size: inherit;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/* Quote blocks */
|
|
160
|
+
.np-content-quote {
|
|
161
|
+
border-left: 4px solid var(--np-color-primary);
|
|
162
|
+
padding: 1rem 1.5rem;
|
|
163
|
+
margin: 1.5rem 0;
|
|
164
|
+
font-style: italic;
|
|
165
|
+
color: var(--np-color-quote-text);
|
|
166
|
+
background-color: var(--np-color-quote-bg);
|
|
167
|
+
border-radius: 0 0.375rem 0.375rem 0;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
.np-content-quote p {
|
|
171
|
+
margin: 0;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
.np-content-quote cite {
|
|
175
|
+
display: block;
|
|
176
|
+
margin-top: 0.75rem;
|
|
177
|
+
font-size: 0.875rem;
|
|
178
|
+
font-style: normal;
|
|
179
|
+
font-weight: 500;
|
|
180
|
+
color: var(--np-color-muted);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/* Divider blocks */
|
|
184
|
+
.np-content-divider {
|
|
185
|
+
border: none;
|
|
186
|
+
border-top: 2px solid var(--np-color-border);
|
|
187
|
+
margin: 2rem 0;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/* HTML blocks */
|
|
191
|
+
.np-content-html {
|
|
192
|
+
margin: 1.5rem 0;
|
|
193
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/* NotPressed Gallery Styles
|
|
2
|
+
* Styles for gallery content blocks and lightbox overlay.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/* Gallery grid */
|
|
6
|
+
.np-content-gallery {
|
|
7
|
+
display: grid;
|
|
8
|
+
grid-template-columns: repeat(var(--np-gallery-columns, 3), 1fr);
|
|
9
|
+
gap: 1rem;
|
|
10
|
+
margin: 1.5rem 0;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/* Gallery item */
|
|
14
|
+
.np-gallery-item {
|
|
15
|
+
overflow: hidden;
|
|
16
|
+
border-radius: 0.375rem;
|
|
17
|
+
cursor: pointer;
|
|
18
|
+
transition: transform 0.2s ease;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.np-gallery-item:hover {
|
|
22
|
+
transform: scale(1.03);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.np-gallery-item img {
|
|
26
|
+
width: 100%;
|
|
27
|
+
aspect-ratio: 1 / 1;
|
|
28
|
+
object-fit: cover;
|
|
29
|
+
display: block;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/* Gallery caption */
|
|
33
|
+
.np-gallery-caption {
|
|
34
|
+
text-align: center;
|
|
35
|
+
font-size: 0.875rem;
|
|
36
|
+
color: var(--np-color-muted, #64748b);
|
|
37
|
+
padding: 0.5rem 0.25rem;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/* Lightbox overlay */
|
|
41
|
+
.np-lightbox-overlay {
|
|
42
|
+
position: fixed;
|
|
43
|
+
top: 0;
|
|
44
|
+
left: 0;
|
|
45
|
+
width: 100%;
|
|
46
|
+
height: 100%;
|
|
47
|
+
background: rgba(0, 0, 0, 0.9);
|
|
48
|
+
z-index: 9999;
|
|
49
|
+
display: flex;
|
|
50
|
+
align-items: center;
|
|
51
|
+
justify-content: center;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.np-lightbox-overlay[hidden] {
|
|
55
|
+
display: none;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/* Lightbox image */
|
|
59
|
+
.np-lightbox-image {
|
|
60
|
+
max-width: 90vw;
|
|
61
|
+
max-height: 85vh;
|
|
62
|
+
object-fit: contain;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/* Lightbox close button */
|
|
66
|
+
.np-lightbox-close {
|
|
67
|
+
position: absolute;
|
|
68
|
+
top: 1rem;
|
|
69
|
+
right: 1rem;
|
|
70
|
+
background: none;
|
|
71
|
+
border: none;
|
|
72
|
+
color: #fff;
|
|
73
|
+
font-size: 2rem;
|
|
74
|
+
cursor: pointer;
|
|
75
|
+
line-height: 1;
|
|
76
|
+
padding: 0.5rem;
|
|
77
|
+
opacity: 0.8;
|
|
78
|
+
transition: opacity 0.2s;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.np-lightbox-close:hover {
|
|
82
|
+
opacity: 1;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/* Lightbox navigation */
|
|
86
|
+
.np-lightbox-nav {
|
|
87
|
+
position: absolute;
|
|
88
|
+
top: 50%;
|
|
89
|
+
transform: translateY(-50%);
|
|
90
|
+
background: none;
|
|
91
|
+
border: none;
|
|
92
|
+
color: #fff;
|
|
93
|
+
font-size: 2.5rem;
|
|
94
|
+
cursor: pointer;
|
|
95
|
+
padding: 1rem;
|
|
96
|
+
opacity: 0.7;
|
|
97
|
+
transition: opacity 0.2s;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.np-lightbox-nav:hover {
|
|
101
|
+
opacity: 1;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.np-lightbox-nav--prev {
|
|
105
|
+
left: 1rem;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.np-lightbox-nav--next {
|
|
109
|
+
right: 1rem;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/* Responsive: single column on mobile */
|
|
113
|
+
@media (max-width: 639px) {
|
|
114
|
+
.np-content-gallery {
|
|
115
|
+
grid-template-columns: 1fr;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/* Starter Theme Styles */
|
|
2
|
+
|
|
3
|
+
/* Header */
|
|
4
|
+
.np-theme-header {
|
|
5
|
+
background-color: var(--np-color-background, #ffffff);
|
|
6
|
+
border-bottom: 1px solid var(--np-color-border, #e2e8f0);
|
|
7
|
+
padding: 1rem 0;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.np-theme-header-inner {
|
|
11
|
+
max-width: 1200px;
|
|
12
|
+
margin: 0 auto;
|
|
13
|
+
padding: 0 1.5rem;
|
|
14
|
+
display: flex;
|
|
15
|
+
align-items: center;
|
|
16
|
+
justify-content: space-between;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.np-theme-site-name {
|
|
20
|
+
font-size: 1.25rem;
|
|
21
|
+
font-weight: 700;
|
|
22
|
+
color: var(--np-color-text, #1e293b);
|
|
23
|
+
text-decoration: none;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.np-theme-site-name:hover {
|
|
27
|
+
color: var(--np-color-primary, #4f46e5);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/* Main content */
|
|
31
|
+
.np-theme-main {
|
|
32
|
+
min-height: calc(100vh - 140px);
|
|
33
|
+
padding: 2rem 0;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.np-theme-content {
|
|
37
|
+
max-width: 720px;
|
|
38
|
+
margin: 0 auto;
|
|
39
|
+
padding: 0 1.5rem;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.np-theme-content--full-width {
|
|
43
|
+
max-width: 1200px;
|
|
44
|
+
margin: 0 auto;
|
|
45
|
+
padding: 0 1.5rem;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/* Sidebar layout */
|
|
49
|
+
.np-theme-layout-sidebar {
|
|
50
|
+
max-width: 1200px;
|
|
51
|
+
margin: 0 auto;
|
|
52
|
+
padding: 0 1.5rem;
|
|
53
|
+
display: grid;
|
|
54
|
+
grid-template-columns: 1fr 300px;
|
|
55
|
+
gap: 3rem;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.np-theme-sidebar {
|
|
59
|
+
border-left: 1px solid var(--np-color-border, #e2e8f0);
|
|
60
|
+
padding-left: 2rem;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/* Footer */
|
|
64
|
+
.np-theme-footer {
|
|
65
|
+
background-color: var(--np-color-surface, #f8fafc);
|
|
66
|
+
border-top: 1px solid var(--np-color-border, #e2e8f0);
|
|
67
|
+
padding: 2rem 0;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.np-theme-footer-inner {
|
|
71
|
+
max-width: 1200px;
|
|
72
|
+
margin: 0 auto;
|
|
73
|
+
padding: 0 1.5rem;
|
|
74
|
+
text-align: center;
|
|
75
|
+
color: var(--np-color-text_muted, #64748b);
|
|
76
|
+
font-size: 0.875rem;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.np-theme-footer-inner p {
|
|
80
|
+
margin: 0;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/* Links */
|
|
84
|
+
.np-theme-content a,
|
|
85
|
+
.np-theme-content--full-width a {
|
|
86
|
+
color: var(--np-color-primary, #4f46e5);
|
|
87
|
+
text-decoration: underline;
|
|
88
|
+
text-underline-offset: 2px;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.np-theme-content a:hover,
|
|
92
|
+
.np-theme-content--full-width a:hover {
|
|
93
|
+
color: var(--np-color-secondary, #7c3aed);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/* Responsive */
|
|
97
|
+
@media (max-width: 768px) {
|
|
98
|
+
.np-theme-layout-sidebar {
|
|
99
|
+
grid-template-columns: 1fr;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.np-theme-sidebar {
|
|
103
|
+
border-left: none;
|
|
104
|
+
border-top: 1px solid var(--np-color-border, #e2e8f0);
|
|
105
|
+
padding-left: 0;
|
|
106
|
+
padding-top: 2rem;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.np-theme-content,
|
|
110
|
+
.np-theme-content--full-width {
|
|
111
|
+
padding: 0 1rem;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.np-theme-header-inner,
|
|
115
|
+
.np-theme-footer-inner {
|
|
116
|
+
padding: 0 1rem;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module NotPressed
|
|
4
|
+
module Admin
|
|
5
|
+
class BaseController < NotPressed::ApplicationController
|
|
6
|
+
include NotPressed::Admin::Authentication
|
|
7
|
+
|
|
8
|
+
helper NotPressed::AdminHelper
|
|
9
|
+
|
|
10
|
+
layout "not_pressed/admin"
|
|
11
|
+
|
|
12
|
+
before_action :authenticate_admin!
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
|
|
16
|
+
def page_title(title)
|
|
17
|
+
@page_title = title
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module NotPressed
|
|
4
|
+
module Admin
|
|
5
|
+
class CategoriesController < BaseController
|
|
6
|
+
before_action :set_category, only: %i[update destroy]
|
|
7
|
+
|
|
8
|
+
def index
|
|
9
|
+
page_title "Categories"
|
|
10
|
+
@categories = NotPressed::Category.ordered
|
|
11
|
+
@category = NotPressed::Category.new
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def create
|
|
15
|
+
@category = NotPressed::Category.new(category_params)
|
|
16
|
+
|
|
17
|
+
if @category.save
|
|
18
|
+
redirect_to admin_categories_path, notice: "Category created successfully."
|
|
19
|
+
else
|
|
20
|
+
page_title "Categories"
|
|
21
|
+
@categories = NotPressed::Category.ordered
|
|
22
|
+
render :index, status: :unprocessable_entity
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def update
|
|
27
|
+
if @category.update(category_params)
|
|
28
|
+
redirect_to admin_categories_path, notice: "Category updated successfully."
|
|
29
|
+
else
|
|
30
|
+
page_title "Categories"
|
|
31
|
+
@categories = NotPressed::Category.ordered
|
|
32
|
+
@category = NotPressed::Category.new
|
|
33
|
+
render :index, status: :unprocessable_entity
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def destroy
|
|
38
|
+
@category.destroy
|
|
39
|
+
redirect_to admin_categories_path, notice: "Category deleted successfully."
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def set_category
|
|
45
|
+
@category = NotPressed::Category.find(params[:id])
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def category_params
|
|
49
|
+
params.require(:category).permit(:name, :description)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module NotPressed
|
|
4
|
+
module Admin
|
|
5
|
+
class ContentBlocksController < BaseController
|
|
6
|
+
before_action :set_page
|
|
7
|
+
before_action :set_block, only: %i[update destroy]
|
|
8
|
+
|
|
9
|
+
def create
|
|
10
|
+
next_position = @page.content_blocks.maximum(:position).to_i + 1
|
|
11
|
+
@block = @page.content_blocks.build(
|
|
12
|
+
block_type: params[:block_type],
|
|
13
|
+
content: NotPressed::ContentBlock.default_content_for(params[:block_type]),
|
|
14
|
+
position: next_position
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
respond_to do |format|
|
|
18
|
+
if @block.save
|
|
19
|
+
format.turbo_stream
|
|
20
|
+
format.html { redirect_to edit_admin_page_path(@page), notice: "Block added." }
|
|
21
|
+
else
|
|
22
|
+
format.html { redirect_to edit_admin_page_path(@page), alert: "Could not add block." }
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def update
|
|
28
|
+
respond_to do |format|
|
|
29
|
+
if @block.update(block_params)
|
|
30
|
+
format.turbo_stream { head :ok }
|
|
31
|
+
format.html { redirect_to edit_admin_page_path(@page), notice: "Block updated." }
|
|
32
|
+
else
|
|
33
|
+
format.html { redirect_to edit_admin_page_path(@page), alert: "Could not update block." }
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def destroy
|
|
39
|
+
@block.destroy
|
|
40
|
+
|
|
41
|
+
respond_to do |format|
|
|
42
|
+
format.turbo_stream
|
|
43
|
+
format.html { redirect_to edit_admin_page_path(@page), notice: "Block removed." }
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def reorder
|
|
48
|
+
(params[:block_ids] || []).each_with_index do |block_id, index|
|
|
49
|
+
@page.content_blocks.where(id: block_id).update_all(position: index)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
respond_to do |format|
|
|
53
|
+
format.turbo_stream { head :ok }
|
|
54
|
+
format.html { redirect_to edit_admin_page_path(@page), notice: "Blocks reordered." }
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
private
|
|
59
|
+
|
|
60
|
+
def set_page
|
|
61
|
+
@page = NotPressed::Page.find(params[:page_id])
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def set_block
|
|
65
|
+
@block = @page.content_blocks.find(params[:id])
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def block_params
|
|
69
|
+
params.require(:content_block).permit(:position, content: {})
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module NotPressed
|
|
4
|
+
module Admin
|
|
5
|
+
class DashboardController < BaseController
|
|
6
|
+
def index
|
|
7
|
+
@page_title = "Dashboard"
|
|
8
|
+
@total_pages = NotPressed::Page.count
|
|
9
|
+
@published_pages = NotPressed::Page.published.count
|
|
10
|
+
@draft_pages = NotPressed::Page.draft.count
|
|
11
|
+
@total_blocks = NotPressed::ContentBlock.count
|
|
12
|
+
@total_media = NotPressed::MediaAttachment.count
|
|
13
|
+
@total_forms = NotPressed::Form.count
|
|
14
|
+
@active_forms = NotPressed::Form.status_active.count
|
|
15
|
+
@recent_pages = NotPressed::Page.order(updated_at: :desc).limit(10)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module NotPressed
|
|
4
|
+
module Admin
|
|
5
|
+
class FormsController < BaseController
|
|
6
|
+
before_action :set_form, only: %i[edit update destroy submissions export_csv]
|
|
7
|
+
|
|
8
|
+
def index
|
|
9
|
+
page_title "Forms"
|
|
10
|
+
@forms = NotPressed::Form.ordered
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def new
|
|
14
|
+
page_title "New Form"
|
|
15
|
+
@form = NotPressed::Form.new
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def create
|
|
19
|
+
@form = NotPressed::Form.new(form_params)
|
|
20
|
+
|
|
21
|
+
if @form.save
|
|
22
|
+
redirect_to edit_admin_form_path(@form), notice: "Form created successfully."
|
|
23
|
+
else
|
|
24
|
+
page_title "New Form"
|
|
25
|
+
render :new, status: :unprocessable_entity
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def edit
|
|
30
|
+
page_title "Edit: #{@form.name}"
|
|
31
|
+
@form_fields = @form.form_fields.ordered
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def update
|
|
35
|
+
if @form.update(form_params)
|
|
36
|
+
redirect_to edit_admin_form_path(@form), notice: "Form updated successfully."
|
|
37
|
+
else
|
|
38
|
+
page_title "Edit: #{@form.name}"
|
|
39
|
+
render :edit, status: :unprocessable_entity
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def destroy
|
|
44
|
+
@form.destroy
|
|
45
|
+
redirect_to admin_forms_path, notice: "Form deleted successfully."
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def submissions
|
|
49
|
+
page_title "Submissions: #{@form.name}"
|
|
50
|
+
@submissions = @form.form_submissions.recent
|
|
51
|
+
@field_labels = @form.form_fields.ordered.pluck(:label)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def export_csv
|
|
55
|
+
@submissions = @form.form_submissions.recent
|
|
56
|
+
field_labels = @form.form_fields.ordered.pluck(:label)
|
|
57
|
+
|
|
58
|
+
csv_data = generate_csv(@submissions, field_labels)
|
|
59
|
+
send_data csv_data, filename: "#{@form.slug}-submissions-#{Date.current}.csv", type: "text/csv"
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
private
|
|
63
|
+
|
|
64
|
+
def set_form
|
|
65
|
+
@form = NotPressed::Form.find(params[:id])
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def generate_csv(submissions, field_labels)
|
|
69
|
+
require "csv"
|
|
70
|
+
CSV.generate(headers: true) do |csv|
|
|
71
|
+
csv << (field_labels + ["Submitted At"])
|
|
72
|
+
submissions.each do |submission|
|
|
73
|
+
row = field_labels.map { |label| submission.data[label] }
|
|
74
|
+
row << submission.submitted_at&.strftime("%Y-%m-%d %H:%M:%S")
|
|
75
|
+
csv << row
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def form_params
|
|
81
|
+
params.require(:form).permit(:name, :description, :email_recipient, :redirect_url, :success_message, :status,
|
|
82
|
+
form_fields_attributes: [:id, :label, :field_type, :required, :placeholder, :options, :position, :_destroy])
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|