first commit, largely copied volunteers
0
app/assets/builds/.keep
Normal file
4
app/assets/config/manifest.js
Normal file
@ -0,0 +1,4 @@
|
||||
//= link_tree ../images
|
||||
//= link_directory ../stylesheets .css
|
||||
//= link_tree ../../javascript .js
|
||||
//= link_tree ../builds
|
0
app/assets/images/.keep
Normal file
BIN
app/assets/images/fallback/default.png
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
app/assets/images/fb.webp
Normal file
After Width: | Height: | Size: 1014 B |
BIN
app/assets/images/feenix_lintu.webp
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
app/assets/images/merged/1.webp
Normal file
After Width: | Height: | Size: 64 KiB |
BIN
app/assets/images/merged/11.webp
Normal file
After Width: | Height: | Size: 96 KiB |
BIN
app/assets/images/merged/119.jpg
Normal file
After Width: | Height: | Size: 95 KiB |
BIN
app/assets/images/merged/22.png
Normal file
After Width: | Height: | Size: 1021 KiB |
BIN
app/assets/images/merged/23.webp
Normal file
After Width: | Height: | Size: 44 KiB |
BIN
app/assets/images/merged/3.webp
Normal file
After Width: | Height: | Size: 95 KiB |
BIN
app/assets/images/merged/33.webp
Normal file
After Width: | Height: | Size: 29 KiB |
BIN
app/assets/images/merged/40.webp
Normal file
After Width: | Height: | Size: 103 KiB |
BIN
app/assets/images/merged/45.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
app/assets/images/merged/73.jpg
Normal file
After Width: | Height: | Size: 628 KiB |
BIN
app/assets/images/merged/section_preview/section_events.jpg
Normal file
After Width: | Height: | Size: 534 KiB |
BIN
app/assets/images/merged/section_preview/section_pictures.png
Normal file
After Width: | Height: | Size: 612 KiB |
BIN
app/assets/images/merged/section_preview/section_story.png
Normal file
After Width: | Height: | Size: 204 KiB |
BIN
app/assets/images/no_image.png
Normal file
After Width: | Height: | Size: 23 KiB |
18
app/assets/stylesheets/application.css
Normal file
@ -0,0 +1,18 @@
|
||||
/*
|
||||
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
||||
* listed below.
|
||||
*
|
||||
* Any CSS (and SCSS, if configured) file within this directory, lib/assets/stylesheets, or any plugin's
|
||||
* vendor/assets/stylesheets directory can be referenced here using a relative path.
|
||||
*
|
||||
* You're free to add application-wide styles to this file and they'll appear at the bottom of the
|
||||
* compiled file so the styles you add here take precedence over styles defined in any other CSS
|
||||
* files in this directory. Styles in this file should be added after the last require_* statement.
|
||||
* It is generally better to create a new file per style scope.
|
||||
*
|
||||
*= require_self
|
||||
*= require glightbox.min
|
||||
*/
|
||||
#thredded--container{
|
||||
max-width: 60rem;
|
||||
}
|
2
app/assets/stylesheets/application.tailwind.css
Normal file
@ -0,0 +1,2 @@
|
||||
@import "tailwind_base";
|
||||
@import "merged_tailwind_styles";
|
1
app/assets/stylesheets/glightbox.min.css
vendored
Normal file
@ -0,0 +1 @@
|
||||
.glightbox-container{width:100%;height:100%;position:fixed;top:0;left:0;z-index:999999!important;overflow:hidden;-ms-touch-action:none;touch-action:none;-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%;-ms-text-size-adjust:100%;text-size-adjust:100%;-webkit-backface-visibility:hidden;backface-visibility:hidden;outline:0}.glightbox-container.inactive{display:none}.glightbox-container .gcontainer{position:relative;width:100%;height:100%;z-index:9999;overflow:hidden}.glightbox-container .gslider{-webkit-transition:-webkit-transform .4s ease;transition:-webkit-transform .4s ease;transition:transform .4s ease;transition:transform .4s ease,-webkit-transform .4s ease;height:100%;left:0;top:0;width:100%;position:relative;overflow:hidden;display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}.glightbox-container .gslide{width:100%;position:absolute;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;opacity:0}.glightbox-container .gslide.current{opacity:1;z-index:99999;position:relative}.glightbox-container .gslide.prev{opacity:1;z-index:9999}.glightbox-container .gslide-inner-content{width:100%}.glightbox-container .ginner-container{position:relative;width:100%;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;max-width:100%;margin:auto;height:100vh}.glightbox-container .ginner-container.gvideo-container{width:100%}.glightbox-container .ginner-container.desc-bottom,.glightbox-container .ginner-container.desc-top{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.glightbox-container .ginner-container.desc-left,.glightbox-container .ginner-container.desc-right{max-width:100%!important}.gslide iframe,.gslide video{outline:0!important;border:none;min-height:165px;-webkit-overflow-scrolling:touch;-ms-touch-action:auto;touch-action:auto}.gslide:not(.current){pointer-events:none}.gslide-image{-webkit-box-align:center;-ms-flex-align:center;align-items:center}.gslide-image img{max-height:100vh;display:block;padding:0;float:none;outline:0;border:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;max-width:100vw;width:auto;height:auto;-o-object-fit:cover;object-fit:cover;-ms-touch-action:none;touch-action:none;margin:auto;min-width:200px}.desc-bottom .gslide-image img,.desc-top .gslide-image img{width:auto}.desc-left .gslide-image img,.desc-right .gslide-image img{width:auto;max-width:100%}.gslide-image img.zoomable{position:relative}.gslide-image img.dragging{cursor:-webkit-grabbing!important;cursor:grabbing!important;-webkit-transition:none;transition:none}.gslide-video{position:relative;max-width:100vh;width:100%!important}.gslide-video .plyr__poster-enabled.plyr--loading .plyr__poster{display:none}.gslide-video .gvideo-wrapper{width:100%;margin:auto}.gslide-video::before{content:'';position:absolute;width:100%;height:100%;background:rgba(255,0,0,.34);display:none}.gslide-video.playing::before{display:none}.gslide-video.fullscreen{max-width:100%!important;min-width:100%;height:75vh}.gslide-video.fullscreen video{max-width:100%!important;width:100%!important}.gslide-inline{background:#fff;text-align:left;max-height:calc(100vh - 40px);overflow:auto;max-width:100%;margin:auto}.gslide-inline .ginlined-content{padding:20px;width:100%}.gslide-inline .dragging{cursor:-webkit-grabbing!important;cursor:grabbing!important;-webkit-transition:none;transition:none}.ginlined-content{overflow:auto;display:block!important;opacity:1}.gslide-external{display:-webkit-box;display:-ms-flexbox;display:flex;width:100%;min-width:100%;background:#fff;padding:0;overflow:auto;max-height:75vh;height:100%}.gslide-media{display:-webkit-box;display:-ms-flexbox;display:flex;width:auto}.zoomed .gslide-media{-webkit-box-shadow:none!important;box-shadow:none!important}.desc-bottom .gslide-media,.desc-top .gslide-media{margin:0 auto;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.gslide-description{position:relative;-webkit-box-flex:1;-ms-flex:1 0 100%;flex:1 0 100%}.gslide-description.description-left,.gslide-description.description-right{max-width:100%}.gslide-description.description-bottom,.gslide-description.description-top{margin:0 auto;width:100%}.gslide-description p{margin-bottom:12px}.gslide-description p:last-child{margin-bottom:0}.zoomed .gslide-description{display:none}.glightbox-button-hidden{display:none}.glightbox-mobile .glightbox-container .gslide-description{height:auto!important;width:100%;position:absolute;bottom:0;padding:19px 11px;max-width:100vw!important;-webkit-box-ordinal-group:3!important;-ms-flex-order:2!important;order:2!important;max-height:78vh;overflow:auto!important;background:-webkit-gradient(linear,left top,left bottom,from(rgba(0,0,0,0)),to(rgba(0,0,0,.75)));background:linear-gradient(to bottom,rgba(0,0,0,0) 0,rgba(0,0,0,.75) 100%);-webkit-transition:opacity .3s linear;transition:opacity .3s linear;padding-bottom:50px}.glightbox-mobile .glightbox-container .gslide-title{color:#fff;font-size:1em}.glightbox-mobile .glightbox-container .gslide-desc{color:#a1a1a1}.glightbox-mobile .glightbox-container .gslide-desc a{color:#fff;font-weight:700}.glightbox-mobile .glightbox-container .gslide-desc *{color:inherit}.glightbox-mobile .glightbox-container .gslide-desc .desc-more{color:#fff;opacity:.4}.gdesc-open .gslide-media{-webkit-transition:opacity .5s ease;transition:opacity .5s ease;opacity:.4}.gdesc-open .gdesc-inner{padding-bottom:30px}.gdesc-closed .gslide-media{-webkit-transition:opacity .5s ease;transition:opacity .5s ease;opacity:1}.greset{-webkit-transition:all .3s ease;transition:all .3s ease}.gabsolute{position:absolute}.grelative{position:relative}.glightbox-desc{display:none!important}.glightbox-open{overflow:hidden}.gloader{height:25px;width:25px;-webkit-animation:lightboxLoader .8s infinite linear;animation:lightboxLoader .8s infinite linear;border:2px solid #fff;border-right-color:transparent;border-radius:50%;position:absolute;display:block;z-index:9999;left:0;right:0;margin:0 auto;top:47%}.goverlay{width:100%;height:calc(100vh + 1px);position:fixed;top:-1px;left:0;background:#000;will-change:opacity}.glightbox-mobile .goverlay{background:#000}.gclose,.gnext,.gprev{z-index:99999;cursor:pointer;width:26px;height:44px;border:none;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.gclose svg,.gnext svg,.gprev svg{display:block;width:25px;height:auto;margin:0;padding:0}.gclose.disabled,.gnext.disabled,.gprev.disabled{opacity:.1}.gclose .garrow,.gnext .garrow,.gprev .garrow{stroke:#fff}.gbtn.focused{outline:2px solid #0f3d81}iframe.wait-autoplay{opacity:0}.glightbox-closing .gclose,.glightbox-closing .gnext,.glightbox-closing .gprev{opacity:0!important}.glightbox-clean .gslide-description{background:#fff}.glightbox-clean .gdesc-inner{padding:22px 20px}.glightbox-clean .gslide-title{font-size:1em;font-weight:400;font-family:arial;color:#000;margin-bottom:19px;line-height:1.4em}.glightbox-clean .gslide-desc{font-size:.86em;margin-bottom:0;font-family:arial;line-height:1.4em}.glightbox-clean .gslide-video{background:#000}.glightbox-clean .gclose,.glightbox-clean .gnext,.glightbox-clean .gprev{background-color:rgba(0,0,0,.75);border-radius:4px}.glightbox-clean .gclose path,.glightbox-clean .gnext path,.glightbox-clean .gprev path{fill:#fff}.glightbox-clean .gprev{position:absolute;top:-100%;left:30px;width:40px;height:50px}.glightbox-clean .gnext{position:absolute;top:-100%;right:30px;width:40px;height:50px}.glightbox-clean .gclose{width:35px;height:35px;top:15px;right:10px;position:absolute}.glightbox-clean .gclose svg{width:18px;height:auto}.glightbox-clean .gclose:hover{opacity:1}.gfadeIn{-webkit-animation:gfadeIn .5s ease;animation:gfadeIn .5s ease}.gfadeOut{-webkit-animation:gfadeOut .5s ease;animation:gfadeOut .5s ease}.gslideOutLeft{-webkit-animation:gslideOutLeft .3s ease;animation:gslideOutLeft .3s ease}.gslideInLeft{-webkit-animation:gslideInLeft .3s ease;animation:gslideInLeft .3s ease}.gslideOutRight{-webkit-animation:gslideOutRight .3s ease;animation:gslideOutRight .3s ease}.gslideInRight{-webkit-animation:gslideInRight .3s ease;animation:gslideInRight .3s ease}.gzoomIn{-webkit-animation:gzoomIn .5s ease;animation:gzoomIn .5s ease}.gzoomOut{-webkit-animation:gzoomOut .5s ease;animation:gzoomOut .5s ease}@-webkit-keyframes lightboxLoader{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes lightboxLoader{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@-webkit-keyframes gfadeIn{from{opacity:0}to{opacity:1}}@keyframes gfadeIn{from{opacity:0}to{opacity:1}}@-webkit-keyframes gfadeOut{from{opacity:1}to{opacity:0}}@keyframes gfadeOut{from{opacity:1}to{opacity:0}}@-webkit-keyframes gslideInLeft{from{opacity:0;-webkit-transform:translate3d(-60%,0,0);transform:translate3d(-60%,0,0)}to{visibility:visible;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1}}@keyframes gslideInLeft{from{opacity:0;-webkit-transform:translate3d(-60%,0,0);transform:translate3d(-60%,0,0)}to{visibility:visible;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1}}@-webkit-keyframes gslideOutLeft{from{opacity:1;visibility:visible;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}to{-webkit-transform:translate3d(-60%,0,0);transform:translate3d(-60%,0,0);opacity:0;visibility:hidden}}@keyframes gslideOutLeft{from{opacity:1;visibility:visible;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}to{-webkit-transform:translate3d(-60%,0,0);transform:translate3d(-60%,0,0);opacity:0;visibility:hidden}}@-webkit-keyframes gslideInRight{from{opacity:0;visibility:visible;-webkit-transform:translate3d(60%,0,0);transform:translate3d(60%,0,0)}to{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1}}@keyframes gslideInRight{from{opacity:0;visibility:visible;-webkit-transform:translate3d(60%,0,0);transform:translate3d(60%,0,0)}to{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1}}@-webkit-keyframes gslideOutRight{from{opacity:1;visibility:visible;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}to{-webkit-transform:translate3d(60%,0,0);transform:translate3d(60%,0,0);opacity:0}}@keyframes gslideOutRight{from{opacity:1;visibility:visible;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}to{-webkit-transform:translate3d(60%,0,0);transform:translate3d(60%,0,0);opacity:0}}@-webkit-keyframes gzoomIn{from{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}to{opacity:1}}@keyframes gzoomIn{from{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}to{opacity:1}}@-webkit-keyframes gzoomOut{from{opacity:1}50%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}to{opacity:0}}@keyframes gzoomOut{from{opacity:1}50%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}to{opacity:0}}@media (min-width:769px){.glightbox-container .ginner-container{width:auto;height:auto;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.glightbox-container .ginner-container.desc-top .gslide-description{-webkit-box-ordinal-group:1;-ms-flex-order:0;order:0}.glightbox-container .ginner-container.desc-top .gslide-image,.glightbox-container .ginner-container.desc-top .gslide-image img{-webkit-box-ordinal-group:2;-ms-flex-order:1;order:1}.glightbox-container .ginner-container.desc-left .gslide-description{-webkit-box-ordinal-group:1;-ms-flex-order:0;order:0}.glightbox-container .ginner-container.desc-left .gslide-image{-webkit-box-ordinal-group:2;-ms-flex-order:1;order:1}.gslide-image img{max-height:97vh;max-width:100%}.gslide-image img.zoomable{cursor:-webkit-zoom-in;cursor:zoom-in}.zoomed .gslide-image img.zoomable{cursor:-webkit-grab;cursor:grab}.gslide-inline{max-height:95vh}.gslide-external{max-height:100vh}.gslide-description.description-left,.gslide-description.description-right{max-width:275px}.glightbox-open{height:auto}.goverlay{background:rgba(0,0,0,.92)}.glightbox-clean .gslide-media{-webkit-box-shadow:1px 2px 9px 0 rgba(0,0,0,.65);box-shadow:1px 2px 9px 0 rgba(0,0,0,.65)}.glightbox-clean .description-left .gdesc-inner,.glightbox-clean .description-right .gdesc-inner{position:absolute;height:100%;overflow-y:auto}.glightbox-clean .gclose,.glightbox-clean .gnext,.glightbox-clean .gprev{background-color:rgba(0,0,0,.32)}.glightbox-clean .gclose:hover,.glightbox-clean .gnext:hover,.glightbox-clean .gprev:hover{background-color:rgba(0,0,0,.7)}.glightbox-clean .gprev{top:45%}.glightbox-clean .gnext{top:45%}}@media (min-width:992px){.glightbox-clean .gclose{opacity:.7;right:20px}}@media screen and (max-height:420px){.goverlay{background:#000}}
|
21
app/assets/stylesheets/merged_tailwind_styles.css
Normal file
@ -0,0 +1,21 @@
|
||||
.prose {
|
||||
max-width: 100%;
|
||||
color: inherit;
|
||||
--tw-prose-bullets: #6b7280;
|
||||
--tw-prose-headings: inherit;
|
||||
}
|
||||
|
||||
@layer components {
|
||||
.button {
|
||||
@apply inline-block rounded-lg px-3 py-2 text-base font-medium border border-gray-500 hover:border-black;
|
||||
}
|
||||
.change {
|
||||
@apply bg-cyan-200;
|
||||
}
|
||||
.remove {
|
||||
@apply bg-red-200;
|
||||
}
|
||||
.action {
|
||||
@apply bg-green-200;
|
||||
}
|
||||
}
|
3
app/assets/stylesheets/tailwind_base.css
Normal file
@ -0,0 +1,3 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
18
app/controllers/application_controller.rb
Normal file
@ -0,0 +1,18 @@
|
||||
class ApplicationController < ActionController::Base
|
||||
before_action :configure_permitted_parameters, if: :devise_controller?
|
||||
|
||||
include Pundit::Authorization
|
||||
alias :current_user :current_member
|
||||
rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
|
||||
|
||||
protected
|
||||
|
||||
def configure_permitted_parameters
|
||||
devise_parameter_sanitizer.permit(:sign_up, keys: [:name])
|
||||
end
|
||||
|
||||
def user_not_authorized
|
||||
flash[:alert] = "You are not authorized to perform this action."
|
||||
redirect_back(fallback_location: root_path)
|
||||
end
|
||||
end
|
0
app/controllers/concerns/.keep
Normal file
59
app/controllers/members_controller.rb
Normal file
@ -0,0 +1,59 @@
|
||||
class MembersController < ApplicationController
|
||||
before_action :set_member, only: %i[ show edit update destroy ]
|
||||
|
||||
# GET /members
|
||||
def index
|
||||
@members = Member.public_scope.
|
||||
page params[:page]
|
||||
end
|
||||
|
||||
def timeline
|
||||
@members = Member.visible_scope.order(:name).page params[:page]
|
||||
end
|
||||
|
||||
# GET /members/1
|
||||
def show
|
||||
end
|
||||
|
||||
# GET /members/1/edit
|
||||
def edit
|
||||
end
|
||||
|
||||
# POST /members
|
||||
def create
|
||||
@member = Member.new(member_params)
|
||||
|
||||
if @member.save
|
||||
redirect_to @member, notice: "Member was successfully created."
|
||||
else
|
||||
render :new, status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
# PATCH/PUT /members/1
|
||||
def update
|
||||
if @member.update(member_params)
|
||||
redirect_to @member, notice: "Member was successfully updated."
|
||||
else
|
||||
render :edit, status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
# DELETE /members/1
|
||||
def destroy
|
||||
@member.destroy
|
||||
redirect_to members_url, notice: "Member was successfully destroyed."
|
||||
end
|
||||
|
||||
private
|
||||
# Use callbacks to share common setup or constraints between actions.
|
||||
def set_member
|
||||
@member = Member.find(params[:id])
|
||||
end
|
||||
|
||||
# Only allow a list of trusted parameters through.
|
||||
def member_params
|
||||
params.require(:member).permit(:name, :public, :bio , :picture,
|
||||
:picture_cache )
|
||||
end
|
||||
end
|
56
app/controllers/pictures_controller.rb
Normal file
@ -0,0 +1,56 @@
|
||||
class PicturesController < ApplicationController
|
||||
before_action :set_picture, only: %i[ show edit update destroy ]
|
||||
|
||||
def index
|
||||
@q = Picture.ransack(params[:q])
|
||||
@q.sorts = 'created_at desc' if @q.sorts.empty?
|
||||
@pictures = @q.result(distinct: true).page( params[:page])
|
||||
end
|
||||
|
||||
def show
|
||||
end
|
||||
|
||||
def new
|
||||
@picture = Picture.new
|
||||
end
|
||||
|
||||
def edit
|
||||
authorize @picture
|
||||
end
|
||||
|
||||
def create
|
||||
@picture = Picture.new(picture_params)
|
||||
@picture.member = current_member
|
||||
|
||||
if @picture.save
|
||||
redirect_to @picture, notice: "Picture was successfully created."
|
||||
else
|
||||
render :new, status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
def update
|
||||
authorize @picture
|
||||
if @picture.update(picture_params)
|
||||
redirect_to @picture, notice: "Picture was successfully updated."
|
||||
else
|
||||
render :edit, status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
authorize @picture
|
||||
@picture.destroy
|
||||
redirect_to pictures_url, notice: "Picture was successfully destroyed."
|
||||
end
|
||||
|
||||
private
|
||||
def set_picture
|
||||
@picture = Picture.find(params[:id])
|
||||
end
|
||||
|
||||
def picture_params
|
||||
params.require(:picture).permit(:picture,:picture_cache ,:text,
|
||||
:happened , :member_id)
|
||||
end
|
||||
end
|
70
app/controllers/registrations_controller.rb
Normal file
@ -0,0 +1,70 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class RegistrationsController < Devise::RegistrationsController
|
||||
# before_action :configure_sign_up_params, only: [:create]
|
||||
# before_action :configure_account_update_params, only: [:update]
|
||||
prepend_before_action :authenticate_scope!, only: [:edit_email]
|
||||
|
||||
def new
|
||||
build_resource
|
||||
super
|
||||
end
|
||||
|
||||
def create
|
||||
if message = math_check
|
||||
puts message
|
||||
flash.now.alert = message
|
||||
build_resource(sign_up_params)
|
||||
render :new
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
def edit_email
|
||||
build_resource
|
||||
end
|
||||
|
||||
def edit
|
||||
build_resource
|
||||
super
|
||||
end
|
||||
|
||||
def update
|
||||
super
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def math_check
|
||||
return "no cheatin" unless bot = params[:bot]
|
||||
return "No food" unless fudder = bot[:fudder]
|
||||
key = fudder.to_i / 2
|
||||
answer = bot[:challenge]
|
||||
return "Plase enter the bot challenge" if answer.blank?
|
||||
if( (2*key + 1).to_s != answer )
|
||||
return "Check the maths, tip, it wasn't #{answer}"
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
# If you have extra params to permit, append them to the sanitizer.
|
||||
# def configure_sign_up_params
|
||||
# devise_parameter_sanitizer.permit(:sign_up, keys: [:attribute])
|
||||
# end
|
||||
|
||||
# If you have extra params to permit, append them to the sanitizer.
|
||||
# def configure_account_update_params
|
||||
# devise_parameter_sanitizer.permit(:account_update, keys: [:attribute])
|
||||
# end
|
||||
|
||||
# The path used after sign up.
|
||||
def after_sign_up_path_for(resource)
|
||||
super(resource)
|
||||
end
|
||||
|
||||
# The path used after sign up for inactive accounts.
|
||||
# def after_inactive_sign_up_path_for(resource)
|
||||
# super(resource)
|
||||
# end
|
||||
end
|
55
app/controllers/stories_controller.rb
Normal file
@ -0,0 +1,55 @@
|
||||
class StoriesController < ApplicationController
|
||||
before_action :set_story, only: %i[ show edit update destroy ]
|
||||
|
||||
def index
|
||||
@q = Story.ransack(params[:q])
|
||||
@q.sorts = 'created_at desc' if @q.sorts.empty?
|
||||
@stories = @q.result(distinct: true).page( params[:page])
|
||||
end
|
||||
|
||||
def show
|
||||
end
|
||||
|
||||
def new
|
||||
@story = Story.new
|
||||
end
|
||||
|
||||
def edit
|
||||
authorize @story
|
||||
end
|
||||
|
||||
def create
|
||||
@story = Story.new(story_params)
|
||||
@story.member = current_member
|
||||
|
||||
if @story.save
|
||||
redirect_to @story, notice: "Story was successfully created."
|
||||
else
|
||||
render :new, status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
def update
|
||||
authorize @story
|
||||
if @story.update(story_params)
|
||||
redirect_to @story, notice: "Story was successfully updated."
|
||||
else
|
||||
render :edit, status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
authorize @story
|
||||
@story.destroy
|
||||
redirect_to stories_url, notice: "Story was successfully destroyed."
|
||||
end
|
||||
|
||||
private
|
||||
def set_story
|
||||
@story = Story.find(params[:id])
|
||||
end
|
||||
|
||||
def story_params
|
||||
params.require(:story).permit(:picture,:picture_cache, :header, :text, :happened)
|
||||
end
|
||||
end
|
76
app/helpers/application_helper.rb
Normal file
@ -0,0 +1,76 @@
|
||||
require "redcarpet"
|
||||
|
||||
module ApplicationHelper
|
||||
|
||||
# different template according to the amount of text
|
||||
def render_story(story)
|
||||
return "" unless story
|
||||
puts story.text.length
|
||||
text_length = story.text.length
|
||||
template = "text"
|
||||
template = "half" if text_length < 500
|
||||
template = "pic" if text_length < 300
|
||||
render partial: "stories/#{template}" , locals: {story: story}
|
||||
end
|
||||
|
||||
def prose_classes
|
||||
classes = "prose lg:prose-lg "
|
||||
classes += "prose-headings:text-inherit "
|
||||
{ class: classes }
|
||||
end
|
||||
|
||||
def renderer
|
||||
options = {hard_wrap: true , autolink: true, no_intra_emphasis: true ,
|
||||
safe_links_only: true, no_styles: true ,
|
||||
link_attributes: { target: '_blank' }}
|
||||
html = Redcarpet::Render::HTML.new(options)
|
||||
Redcarpet::Markdown.new(html, options)
|
||||
end
|
||||
|
||||
def markdown(text)
|
||||
return "" if text.blank?
|
||||
text = text.text unless text.is_a?(String)
|
||||
return "" if text.blank?
|
||||
self.renderer.render(text).html_safe
|
||||
end
|
||||
|
||||
def shorten(text , to = 100)
|
||||
return "" if text.blank?
|
||||
"#{text[0..to]} . . . ".html_safe
|
||||
end
|
||||
def main_app
|
||||
Rails.application.routes.url_helpers
|
||||
end
|
||||
|
||||
def button_classes
|
||||
"mr-3 inline-block rounded-lg px-3 py-2 text-md font-medium border border-gray-500"
|
||||
end
|
||||
|
||||
def rows( text )
|
||||
return 5 if text.blank?
|
||||
text = text.text unless text.is_a?(String)
|
||||
return 5 if text.blank?
|
||||
rows = (text.length / 60).to_i
|
||||
return 5 if rows < 5
|
||||
rows
|
||||
end
|
||||
|
||||
def main_menu
|
||||
[["/members" , "Makerspace"],["/stories" , "Stories"], ["/pictures" , "Gallery"],
|
||||
["/volunteering" , "Volunteering"] ]
|
||||
end
|
||||
def member_memu
|
||||
items =[ [main_app.member_path(current_member) , "Settings"]]
|
||||
if !Rails.env.production?
|
||||
items << [merged.pages_path(), "CMS" ]
|
||||
end
|
||||
items
|
||||
end
|
||||
def mobile_menu
|
||||
if current_member
|
||||
member_memu
|
||||
else
|
||||
[main_app.member_session_path, "Login"]
|
||||
end
|
||||
end
|
||||
end
|
5
app/helpers/heroicon_helper.rb
Normal file
@ -0,0 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module HeroiconHelper
|
||||
include Heroicon::Engine.helpers
|
||||
end
|
20
app/helpers/members_helper.rb
Normal file
@ -0,0 +1,20 @@
|
||||
module MembersHelper
|
||||
|
||||
def picture_for(someone , classes = "")
|
||||
image = someones_path(someone)
|
||||
if someone.respond_to? :name
|
||||
alt = someone.name
|
||||
else
|
||||
alt = ""
|
||||
end
|
||||
image_tag(image , alt: alt , class: classes )
|
||||
end
|
||||
|
||||
def someones_path( someone )
|
||||
if someone.picture.blank?
|
||||
asset_url("no_image.png")
|
||||
else
|
||||
someone.picture.url
|
||||
end
|
||||
end
|
||||
end
|
2
app/helpers/pictures_helper.rb
Normal file
@ -0,0 +1,2 @@
|
||||
module PicturesHelper
|
||||
end
|
2
app/helpers/stories_helper.rb
Normal file
@ -0,0 +1,2 @@
|
||||
module StoriesHelper
|
||||
end
|
10
app/javascript/application.js
Normal file
@ -0,0 +1,10 @@
|
||||
// Configure your import map in config/importmap.rb.
|
||||
// Read more: https://github.com/rails/importmap-rails
|
||||
|
||||
// Currently haml filter does not support modules.
|
||||
// import and make global as workaround
|
||||
//
|
||||
// haml module syntax is really clunky, filing issue and hoping
|
||||
//
|
||||
import GLightbox from 'glightbox';
|
||||
globalThis.GLightbox = GLightbox;
|
7
app/jobs/application_job.rb
Normal file
@ -0,0 +1,7 @@
|
||||
class ApplicationJob < ActiveJob::Base
|
||||
# Automatically retry jobs that encountered a deadlock
|
||||
# retry_on ActiveRecord::Deadlocked
|
||||
|
||||
# Most jobs are safe to ignore if the underlying records are no longer available
|
||||
# discard_on ActiveJob::DeserializationError
|
||||
end
|
4
app/mailers/application_mailer.rb
Normal file
@ -0,0 +1,4 @@
|
||||
class ApplicationMailer < ActionMailer::Base
|
||||
default from: "from@example.com"
|
||||
layout "mailer"
|
||||
end
|
3
app/models/application_record.rb
Normal file
@ -0,0 +1,3 @@
|
||||
class ApplicationRecord < ActiveRecord::Base
|
||||
primary_abstract_class
|
||||
end
|
0
app/models/concerns/.keep
Normal file
35
app/models/member.rb
Normal file
@ -0,0 +1,35 @@
|
||||
class Member < ApplicationRecord
|
||||
after_create :skip_conf!
|
||||
|
||||
def self.public_scope
|
||||
where.not(confirmed_at: nil).where.not(picture: nil)
|
||||
end
|
||||
|
||||
def self.visible_scope
|
||||
where.not(confirmed_at: nil)
|
||||
end
|
||||
|
||||
# Include default devise modules. Others available are:
|
||||
# , :lockable, :timeoutable, :trackable and :omniauthable
|
||||
devise :database_authenticatable, :registerable,:confirmable,
|
||||
:recoverable, :rememberable, :validatable, :async
|
||||
|
||||
def skip_conf!
|
||||
self.confirm if Rails.env.development?
|
||||
end
|
||||
|
||||
mount_uploader :picture, PictureUploader
|
||||
has_many :stories
|
||||
has_many :stories
|
||||
has_many :pictures
|
||||
|
||||
validates :bio, length: { maximum: 1000 }
|
||||
validates :name , length: { minimum: 3 }
|
||||
|
||||
def admin
|
||||
self.email == "torsten@villataika.fi"
|
||||
end
|
||||
def admin?
|
||||
self.email == "torsten@villataika.fi"
|
||||
end
|
||||
end
|
10
app/models/picture.rb
Normal file
@ -0,0 +1,10 @@
|
||||
class Picture < ApplicationRecord
|
||||
belongs_to :member
|
||||
|
||||
mount_uploader :picture, PictureUploader
|
||||
|
||||
validates :text, length: { maximum: 80 }
|
||||
validates :happened, presence: true
|
||||
validates :picture, presence: true
|
||||
|
||||
end
|
13
app/models/story.rb
Normal file
@ -0,0 +1,13 @@
|
||||
class Story < ApplicationRecord
|
||||
belongs_to :member
|
||||
|
||||
mount_uploader :picture, PictureUploader
|
||||
|
||||
validates :text, length: { minimum: 5 , maximum: 1000 }
|
||||
validates :header , length: { minimum: 5 , maximum: 400}
|
||||
validates :picture, presence: true
|
||||
|
||||
def name
|
||||
header
|
||||
end
|
||||
end
|
53
app/policies/application_policy.rb
Normal file
@ -0,0 +1,53 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class ApplicationPolicy
|
||||
attr_reader :member, :record
|
||||
|
||||
def initialize(member, record)
|
||||
@member = member
|
||||
@record = record
|
||||
end
|
||||
|
||||
def index?
|
||||
false
|
||||
end
|
||||
|
||||
def show?
|
||||
false
|
||||
end
|
||||
|
||||
def create?
|
||||
false
|
||||
end
|
||||
|
||||
def new?
|
||||
create?
|
||||
end
|
||||
|
||||
def update?
|
||||
false
|
||||
end
|
||||
|
||||
def edit?
|
||||
update?
|
||||
end
|
||||
|
||||
def destroy?
|
||||
false
|
||||
end
|
||||
|
||||
class Scope
|
||||
def initialize(member, scope)
|
||||
@member = member
|
||||
@scope = scope
|
||||
end
|
||||
|
||||
def resolve
|
||||
raise NotImplementedError, "You must define #resolve in #{self.class}"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :member, :scope
|
||||
end
|
||||
end
|
14
app/policies/edit_own_policy.rb
Normal file
@ -0,0 +1,14 @@
|
||||
# allows to edit/detroy own data
|
||||
# which can be viewed by anyone
|
||||
class EditOwnPolicy < ApplicationPolicy
|
||||
def edit?
|
||||
return true if member.admin?
|
||||
owner?
|
||||
end
|
||||
def owner?
|
||||
member == record.member
|
||||
end
|
||||
alias :update? :edit?
|
||||
alias :destroy? :edit?
|
||||
|
||||
end
|
3
app/policies/picture_policy.rb
Normal file
@ -0,0 +1,3 @@
|
||||
class PicturePolicy < EditOwnPolicy
|
||||
|
||||
end
|
3
app/policies/story_policy.rb
Normal file
@ -0,0 +1,3 @@
|
||||
class StoryPolicy < EditOwnPolicy
|
||||
|
||||
end
|
43
app/uploaders/picture_uploader.rb
Normal file
@ -0,0 +1,43 @@
|
||||
class PictureUploader < CarrierWave::Uploader::Base
|
||||
# Include RMagick or MiniMagick support:
|
||||
# include CarrierWave::RMagick
|
||||
include CarrierWave::MiniMagick
|
||||
|
||||
# Choose what kind of storage to use for this uploader:
|
||||
storage :file
|
||||
# storage :fog
|
||||
|
||||
# Override the directory where uploaded files will be stored.
|
||||
# This is a sensible default for uploaders that are meant to be mounted:
|
||||
def store_dir
|
||||
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
|
||||
end
|
||||
|
||||
# Provide a default URL as a default if there hasn't been a file uploaded:
|
||||
def default_url(*args)
|
||||
# For Rails 3.1+ asset pipeline compatibility:
|
||||
ActionController::Base.helpers.asset_path("fallback/" + [version_name, "default.png"].compact.join('_'))
|
||||
end
|
||||
|
||||
# Process files as they are uploaded:
|
||||
# process scale: [200, 300]
|
||||
#
|
||||
# def scale(width, height)
|
||||
# # do something
|
||||
# end
|
||||
|
||||
# Create different versions of your uploaded files:
|
||||
# version :thumb do
|
||||
# process resize_to_fit: [50, 50]
|
||||
# end
|
||||
|
||||
def content_type_allowlist
|
||||
/image\//
|
||||
end
|
||||
|
||||
# Override the filename of the uploaded files:
|
||||
# Avoid using model.id or version_name here, see uploader/store.rb for details.
|
||||
# def filename
|
||||
# "something.jpg" if original_filename
|
||||
# end
|
||||
end
|
20
app/views/devise/confirmations/new.html.haml
Normal file
@ -0,0 +1,20 @@
|
||||
.flex.justify-center
|
||||
.w-full.max-w-xs.md:max-w-md
|
||||
%h1.font-hairline.mb-6.text-center Resend Confirmation Instructions
|
||||
= form_for(resource, as: resource_name,
|
||||
url: confirmation_path(resource_name),
|
||||
html: { class: "bg-white mb-4 px-8 pt-6 pb-8 rounded shadow-md" ,
|
||||
method: :post }) do |f|
|
||||
= render "devise/shared/error_messages", resource: resource
|
||||
.mb-4
|
||||
= f.label :email, class: "block font-bold mb-2 text-gray-700 text-sm"
|
||||
= f.email_field :email, |
|
||||
autofocus: true, |
|
||||
autocomplete: "email", |
|
||||
value: (resource.pending_reconfirmation? ? resource.unconfirmed_email : resource.email), |
|
||||
class: "appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none shadow focus:shadow-outline" |
|
||||
.mb-4
|
||||
= f.submit "Resend Confirmation Info", |
|
||||
class: "button bg-cyan-700 hover:bg-cyan-500 font-bold text-white focus:outline-none py-2 px-4 rounded focus:shadow-outline w-full" |
|
||||
= render "devise/shared/links"
|
||||
= render "devise/shared/form_footer"
|
14
app/views/devise/mailer/confirmation_instructions.html.haml
Normal file
@ -0,0 +1,14 @@
|
||||
%p
|
||||
Welcome #{@resource.name}!
|
||||
|
||||
%p You can confirm your stay through the link below:
|
||||
%p= link_to 'Confirm my account', confirmation_url(@resource, confirmation_token: @token)
|
||||
|
||||
Remember you can change the dates later, under the Settings.
|
||||
|
||||
You can also set a profile and picture to tell others about yourself.
|
||||
And once at the Hub, you can add pictures and stories to share with others.
|
||||
|
||||
Looking forward to seeing you in person
|
||||
|
||||
The hub team
|
8
app/views/devise/mailer/email_changed.html.haml
Normal file
@ -0,0 +1,8 @@
|
||||
%p
|
||||
Hello #{@email}!
|
||||
- if @resource.try(:unconfirmed_email?)
|
||||
%p
|
||||
We're contacting you to notify you that your email is being changed to #{@resource.unconfirmed_email}.
|
||||
- else
|
||||
%p
|
||||
We're contacting you to notify you that your email has been changed to #{@resource.email}.
|
3
app/views/devise/mailer/password_change.html.haml
Normal file
@ -0,0 +1,3 @@
|
||||
%p
|
||||
Hello #{@resource.email}!
|
||||
%p We're contacting you to notify you that your password has been changed.
|
@ -0,0 +1,6 @@
|
||||
%p
|
||||
Hello #{@resource.email}!
|
||||
%p Someone has requested a link to change your password. You can do this through the link below.
|
||||
%p= link_to 'Change my password', edit_password_url(@resource, reset_password_token: @token)
|
||||
%p If you didn't request this, please ignore this email.
|
||||
%p Your password won't change until you access the link above and create a new one.
|
5
app/views/devise/mailer/unlock_instructions.html.haml
Normal file
@ -0,0 +1,5 @@
|
||||
%p
|
||||
Hello #{@resource.email}!
|
||||
%p Your account has been locked due to an excessive number of unsuccessful sign in attempts.
|
||||
%p Click the link below to unlock your account:
|
||||
%p= link_to 'Unlock my account', unlock_url(@resource, unlock_token: @token)
|
32
app/views/devise/passwords/edit.html.haml
Normal file
@ -0,0 +1,32 @@
|
||||
.flex.justify-center
|
||||
.w-full.max-w-xs.md:max-w-md
|
||||
%h2.font-hairline.mb-6.text-center Change Your Password
|
||||
= form_for(resource,
|
||||
as: resource_name,
|
||||
html: { method: :put, class: "bg-white mb-4 px-8 pt-6 pb-8 rounded shadow-md" },
|
||||
url: password_path(resource_name) ) do |f|
|
||||
= render "devise/shared/error_messages", resource: resource
|
||||
= f.hidden_field :reset_password_token
|
||||
.mb-4
|
||||
= f.label :password, "New Password",
|
||||
class: "block font-bold mb-2 text-gray-700 text-sm"
|
||||
- if @minimum_password_length
|
||||
%small
|
||||
%em.text-gray-600
|
||||
(#{@minimum_password_length} characters minimum)
|
||||
= f.password_field :password, |
|
||||
autofocus: true, |
|
||||
autocomplete: "new-password", |
|
||||
class: "appearance-none border leading-tight focus:outline-none px-3 py-2 rounded shadow focus:shadow-outline text-gray-700 w-full"
|
||||
.mb-4
|
||||
= f.label :password_confirmation,
|
||||
"Confirm New Password",
|
||||
class: "block font-bold mb-2 text-gray-700 text-sm"
|
||||
= f.password_field :password_confirmation, |
|
||||
autocomplete: "off", |
|
||||
class: "shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 mb-3 leading-tight focus:outline-none focus:shadow-outline"
|
||||
.mb-4
|
||||
= f.submit "Change My Password", |
|
||||
class: "button bg-cyan-700 hover:bg-cyan-500 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline w-full"
|
||||
= render "devise/shared/links"
|
||||
= render "devise/shared/form_footer"
|
17
app/views/devise/passwords/new.html.haml
Normal file
@ -0,0 +1,17 @@
|
||||
.flex.justify-center
|
||||
.w-full.max-w-xs.md:max-w-md
|
||||
%h1.font-hairline.mb-6.text-center Forgot your password?
|
||||
= form_for(resource, as: resource_name,
|
||||
html: { method: :post,
|
||||
class: "bg-white mb-4 px-8 pt-6 pb-8 rounded shadow-md" } ,
|
||||
url: password_path(resource_name) ) do |f|
|
||||
= render "devise/shared/error_messages", resource: resource
|
||||
.mb-4
|
||||
= f.label :email, class: "block font-bold mb-2 text-gray-700 text-sm"
|
||||
= f.email_field :email, autofocus: true, autocomplete: "email", |
|
||||
class: "appearance-none border leading-tight focus:outline-none px-3 py-2 rounded shadow focus:shadow-outline text-gray-700 w-full" |
|
||||
.mb-4
|
||||
= f.submit "Send Password Reset Info", |
|
||||
class: "button bg-cyan-700 hover:bg-cyan-500 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline w-full" |
|
||||
= render "devise/shared/links"
|
||||
= render "devise/shared/form_footer"
|
32
app/views/devise/registrations/edit.html.haml
Normal file
@ -0,0 +1,32 @@
|
||||
.flex.justify-center
|
||||
.w-full.max-w-xs.md:max-w-md
|
||||
%h1.font-hairline.mb-6.text-center.text-2xl
|
||||
Change password
|
||||
= form_for(resource, as: resource_name,
|
||||
html: { class: "bg-white mb-4 px-8 pt-6 pb-8 rounded shadow-md",
|
||||
method: :put } ,
|
||||
url: registration_path(resource_name) ) do |f|
|
||||
= render "devise/shared/error_messages", resource: resource
|
||||
- if devise_mapping.confirmable? && resource.pending_reconfirmation?
|
||||
%div
|
||||
Currently waiting confirmation for: #{resource.unconfirmed_email}
|
||||
.mb-4
|
||||
= f.label :new_password, class: "block font-bold mb-2 text-gray-700 text-sm"
|
||||
= f.password_field :password, autocomplete: "new-password", class: "shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 mb-3 leading-tight focus:outline-none focus:shadow-outline"
|
||||
.mb-4
|
||||
= f.label :password_confirmation, class: "block font-bold mb-2 text-gray-700 text-sm"
|
||||
= f.password_field :password_confirmation, |
|
||||
autocomplete: "new-password", |
|
||||
class: "shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 mb-3 leading-tight focus:outline-none focus:shadow-outline" |
|
||||
.mb-4
|
||||
= f.label :current_password, class: "block font-bold mb-2 text-gray-700 text-sm"
|
||||
= f.password_field :current_password, |
|
||||
autocomplete: "current-password", |
|
||||
class: "shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 mb-3 leading-tight focus:outline-none focus:shadow-outline" |
|
||||
.actions
|
||||
= f.submit "Update", class: "button bg-cyan-700 hover:bg-cyan-500 font-bold text-white focus:outline-none py-2 px-4 rounded focus:shadow-outline w-full"
|
||||
.flex.justify-between
|
||||
%p
|
||||
%span= button_to "Delete my account", registration_path(resource_name), data: { confirm: "Are you sure?" }, method: :delete , class: button_classes
|
||||
%button{class: button_classes}
|
||||
= link_to "Back", :back
|
32
app/views/devise/registrations/edit_email.html.haml
Normal file
@ -0,0 +1,32 @@
|
||||
.flex.justify-center
|
||||
.w-full.max-w-xs.md:max-w-md
|
||||
%h1.font-hairline.mb-6.text-center.text-2xl
|
||||
Change your email
|
||||
.text-center.text-lg
|
||||
(requires confirmation)
|
||||
= form_for(resource, as: resource_name,
|
||||
html: { class: "bg-white mb-4 px-8 pt-6 pb-8 rounded shadow-md",
|
||||
method: :put } ,
|
||||
url: registration_path(resource_name) ) do |f|
|
||||
= render "devise/shared/error_messages", resource: resource
|
||||
.mb-4
|
||||
= f.label :new_email, class: "block font-bold mb-2 text-gray-700 text-sm"
|
||||
%br/
|
||||
= f.email_field :email, |
|
||||
autofocus: true, |
|
||||
autocomplete: "email", |
|
||||
class: "appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none shadow focus:shadow-outline" |
|
||||
- if devise_mapping.confirmable? && resource.pending_reconfirmation?
|
||||
%div
|
||||
Currently waiting confirmation for: #{resource.unconfirmed_email}
|
||||
.mb-4
|
||||
= f.label :current_password, class: "block font-bold mb-2 text-gray-700 text-sm"
|
||||
= f.password_field :current_password, |
|
||||
autocomplete: "current-password", |
|
||||
class: "shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 mb-3 leading-tight focus:outline-none focus:shadow-outline" |
|
||||
.actions
|
||||
= f.submit "Update", class: "button bg-cyan-700 hover:bg-cyan-500 font-bold text-white focus:outline-none py-2 px-4 rounded focus:shadow-outline w-full"
|
||||
|
||||
= link_to :back do
|
||||
%button{class: button_classes}
|
||||
Back
|
51
app/views/devise/registrations/new.html.haml
Normal file
@ -0,0 +1,51 @@
|
||||
.flex.justify-center
|
||||
.w-full.max-w-xs.md:max-w-md
|
||||
%h1.font-hairline.mb-6.text-center.text-2xl Confirm your stay
|
||||
= form_for(resource, as: resource_name,
|
||||
html: { class: "bg-white mb-4 px-8 pt-6 pb-8 rounded shadow-md" } ,
|
||||
url: registration_path(resource_name) ) do |f|
|
||||
= render "devise/shared/error_messages", resource: resource
|
||||
.mb-4
|
||||
Please read the
|
||||
= link_to "house rules" , "/house_rules" , class: "underline"
|
||||
if you haven't yet, and let us know of
|
||||
any alergies (food?) or medical conditions (ADHD?) beforehand.
|
||||
.mb-4
|
||||
= f.label :name, class: "block font-bold mb-2 text-gray-700 text-sm"
|
||||
= f.input :name,
|
||||
placeholder: "Pekka",
|
||||
class: "appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none shadow focus:shadow-outline"
|
||||
.mb-4
|
||||
= f.label :email, class: "block font-bold mb-2 text-gray-700 text-sm"
|
||||
= f.email_field :email,
|
||||
autocomplete: "email",
|
||||
placeholder: "user@example.com",
|
||||
class: "appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none shadow focus:shadow-outline"
|
||||
.mb-4
|
||||
= f.label :password, class: "block font-bold mb-2 text-gray-700 text-sm"
|
||||
- if @minimum_password_length
|
||||
%small
|
||||
%em.text-gray-600
|
||||
(#{@minimum_password_length} characters minimum)
|
||||
= f.password_field :password,
|
||||
autocomplete: "new-password",
|
||||
class: "shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 mb-3 leading-tight focus:outline-none focus:shadow-outline"
|
||||
.mb-4
|
||||
= f.label :password_confirmation, class: "block font-bold mb-2 text-gray-700 text-sm"
|
||||
= f.password_field :password_confirmation,
|
||||
autocomplete: "new-password",
|
||||
class: "appearance-none border leading-tight focus:outline-none px-3 py-2 rounded shadow focus:shadow-outline text-gray-700 w-full"
|
||||
.mt-4
|
||||
- challenge = rand(8)
|
||||
= simple_fields_for :bot do |n|
|
||||
= n.input :fudder , as: :hidden , input_html: { value: "#{challenge*2}" }
|
||||
= n.input :challenge, placeholder: "Anti bot question: #{challenge} + #{challenge + 1} == " ,
|
||||
class: " border leading-tight focus:outline-none px-3 py-2 rounded shadow focus:shadow-outline text-gray-700 w-full"
|
||||
.my-4
|
||||
= f.submit "Sign Up",
|
||||
class: "button bg-cyan-700 hover:bg-cyan-700 font-bold text-white focus:outline-none py-2 px-4 rounded focus:shadow-outline w-full"
|
||||
%p
|
||||
After submitting you will receive a confirmation email.
|
||||
Your place will be ensured once you have confirmed your email.
|
||||
= render "devise/shared/links"
|
||||
= render "devise/shared/form_footer"
|
26
app/views/devise/sessions/new.html.haml
Normal file
@ -0,0 +1,26 @@
|
||||
.flex.justify-center
|
||||
.w-full.max-w-xs.md:max-w-md
|
||||
%h1.font-hairline.mb-6.text-center Log In
|
||||
= form_for(resource, url: session_path(resource_name),
|
||||
html: { class: "bg-white mb-4 px-8 pt-6 pb-8 rounded shadow-md" },
|
||||
as: resource_name ) do |f|
|
||||
= render "devise/shared/error_messages", resource: resource
|
||||
.mb-4
|
||||
= f.label :email, class: "block text-gray-700 text-sm font-bold mb-2"
|
||||
= f.email_field :email, autofocus: true, autocomplete: "email",
|
||||
class: "shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight foucs:outline-none focus:shadow-outline"
|
||||
.mb-4
|
||||
= f.label :password, class: "block text-gray-700 text-sm font-bold mb-2"
|
||||
= f.password_field :password,
|
||||
autocomplete: "current-password",
|
||||
class: "shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
|
||||
- if devise_mapping.rememberable?
|
||||
.mb-4
|
||||
= f.check_box :remember_me, class: "mr-2 leading-tight"
|
||||
= f.label :remember_me,
|
||||
class: "align-baseline inline-block text-gray-700 text-sm"
|
||||
.mb-4
|
||||
= f.submit "Log in",
|
||||
class: "button bg-cyan-700 hover:bg-cyan-500 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline w-full"
|
||||
= render "devise/shared/links"
|
||||
= render "devise/shared/form_footer"
|
9
app/views/devise/shared/_error_messages.html.haml
Normal file
@ -0,0 +1,9 @@
|
||||
- if resource.errors.any?
|
||||
#error_explanation
|
||||
%h2.bg-red-100.border-l-4.border-red-500.mb-4.p-4.text-red-700.font-bold
|
||||
= I18n.t("errors.messages.not_saved",
|
||||
count: resource.errors.count,
|
||||
resource: resource.class.model_name.human.downcase)
|
||||
%ul
|
||||
- resource.errors.full_messages.each do |message|
|
||||
%li= message
|
2
app/views/devise/shared/_form_footer.html.haml
Normal file
@ -0,0 +1,2 @@
|
||||
%p.text-center.text-gray-500.text-xs
|
||||
Be a member of Hub Feenix
|
20
app/views/devise/shared/_links.html.haml
Normal file
@ -0,0 +1,20 @@
|
||||
- if controller_name != 'sessions'
|
||||
= link_to "Log in", new_session_path(resource_name), |
|
||||
class: "inline-block align-baseline font-bold text-sm text-blue-500 hover:text-blue-800" |
|
||||
%br/
|
||||
- if devise_mapping.registerable? && controller_name != 'registrations'
|
||||
= link_to "Sign up", new_registration_path(resource_name), |
|
||||
class: "inline-block align-baseline font-bold text-sm text-blue-500 hover:text-blue-800" |
|
||||
%br/
|
||||
- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations'
|
||||
= link_to "Forgot Password?", new_password_path(resource_name), |
|
||||
class: "inline-block align-baseline font-bold text-sm text-blue-500 hover:text-blue-800" |
|
||||
%br/
|
||||
- if devise_mapping.confirmable? && controller_name != 'confirmations'
|
||||
= link_to "Didn't receive confirmation info?", new_confirmation_path(resource_name), |
|
||||
class: "inline-block align-baseline font-bold text-sm text-blue-500 hover:text-blue-800" |
|
||||
%br/
|
||||
- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks'
|
||||
= link_to "Didn't receive unlock info?", new_unlock_path(resource_name), |
|
||||
class: "inline-block align-baseline font-bold text-sm text-blue-500 hover:text-blue-800" |
|
||||
%br/
|
19
app/views/devise/unlocks/new.html.haml
Normal file
@ -0,0 +1,19 @@
|
||||
.flex.justify-center
|
||||
.w-full.max-w-xs
|
||||
%h1.font-hairline.mb-6.text-center Resend Unlock Info
|
||||
= form_for(resource, as: resource_name,
|
||||
html: { class: "bg-white mb-4 px-8 pt-6 pb-8 rounded shadow-md" ,
|
||||
method: :post }
|
||||
url: unlock_path(resource_name) ) do |f|
|
||||
= render "devise/shared/error_messages", resource: resource
|
||||
.mb-4
|
||||
= f.label :email, class: "block font-bold mb-2 text-gray-700 text-sm"
|
||||
= f.email_field :email, |
|
||||
autofocus: true, |
|
||||
autocomplete: "email", |
|
||||
class: "appearance-none border leading-tight focus:outline-none px-3 py-2 rounded shadow focus:shadow-outline text-gray-700 w-full" |
|
||||
.mb-4
|
||||
= f.submit "Resend unlock instructions", |
|
||||
class: "button bg-blue-500 hover:bg-blue-700 font-bold text-white focus:outline-none py-2 px-4 rounded focus:shadow-outline w-full" |
|
||||
= render "devise/shared/links"
|
||||
= render "devise/shared/form_footer"
|
9
app/views/kaminari/_first_page.html.haml
Normal file
@ -0,0 +1,9 @@
|
||||
-# Link to the "First" page
|
||||
-# available local variables
|
||||
-# url: url to the first page
|
||||
-# current_page: a page object for the currently displayed page
|
||||
-# total_pages: total number of pages
|
||||
-# per_page: number of items to fetch per page
|
||||
-# remote: data-remote
|
||||
%span.first
|
||||
= link_to_unless current_page.first?, t('views.pagination.first').html_safe, url, remote: remote
|
8
app/views/kaminari/_gap.html.haml
Normal file
@ -0,0 +1,8 @@
|
||||
-# Non-link tag that stands for skipped pages...
|
||||
-# available local variables
|
||||
-# current_page: a page object for the currently displayed page
|
||||
-# total_pages: total number of pages
|
||||
-# per_page: number of items to fetch per page
|
||||
-# remote: data-remote
|
||||
%span.page.gap
|
||||
= t('views.pagination.truncate').html_safe
|
9
app/views/kaminari/_last_page.html.haml
Normal file
@ -0,0 +1,9 @@
|
||||
-# Link to the "Last" page
|
||||
-# available local variables
|
||||
-# url: url to the last page
|
||||
-# current_page: a page object for the currently displayed page
|
||||
-# total_pages: total number of pages
|
||||
-# per_page: number of items to fetch per page
|
||||
-# remote: data-remote
|
||||
%span.last
|
||||
= link_to_unless current_page.last?, t('views.pagination.last').html_safe, url, remote: remote
|
9
app/views/kaminari/_next_page.html.haml
Normal file
@ -0,0 +1,9 @@
|
||||
-# Link to the "Next" page
|
||||
-# available local variables
|
||||
-# url: url to the next page
|
||||
-# current_page: a page object for the currently displayed page
|
||||
-# total_pages: total number of pages
|
||||
-# per_page: number of items to fetch per page
|
||||
-# remote: data-remote
|
||||
%span.next
|
||||
= link_to_unless current_page.last?, t('views.pagination.next').html_safe, url, rel: 'next', remote: remote
|
10
app/views/kaminari/_page.html.haml
Normal file
@ -0,0 +1,10 @@
|
||||
-# Link showing page number
|
||||
-# available local variables
|
||||
-# page: a page object for "this" page
|
||||
-# url: url to this page
|
||||
-# current_page: a page object for the currently displayed page
|
||||
-# total_pages: total number of pages
|
||||
-# per_page: number of items to fetch per page
|
||||
-# remote: data-remote
|
||||
%span{class: "page#{' current' if page.current?}"}
|
||||
= link_to_unless page.current?, page, url, {remote: remote, rel: page.rel}
|
18
app/views/kaminari/_paginator.html.haml
Normal file
@ -0,0 +1,18 @@
|
||||
-# The container tag
|
||||
-# available local variables
|
||||
-# current_page: a page object for the currently displayed page
|
||||
-# total_pages: total number of pages
|
||||
-# per_page: number of items to fetch per page
|
||||
-# remote: data-remote
|
||||
-# paginator: the paginator that renders the pagination tags inside
|
||||
= paginator.render do
|
||||
%nav.pagination
|
||||
= first_page_tag unless current_page.first?
|
||||
= prev_page_tag unless current_page.first?
|
||||
- each_page do |page|
|
||||
- if page.display_tag?
|
||||
= page_tag page
|
||||
- elsif !page.was_truncated?
|
||||
= gap_tag
|
||||
= next_page_tag unless current_page.last?
|
||||
= last_page_tag unless current_page.last?
|
9
app/views/kaminari/_prev_page.html.haml
Normal file
@ -0,0 +1,9 @@
|
||||
-# Link to the "Previous" page
|
||||
-# available local variables
|
||||
-# url: url to the previous page
|
||||
-# current_page: a page object for the currently displayed page
|
||||
-# total_pages: total number of pages
|
||||
-# per_page: number of items to fetch per page
|
||||
-# remote: data-remote
|
||||
%span.prev
|
||||
= link_to_unless current_page.first?, t('views.pagination.previous').html_safe, url, rel: 'prev', remote: remote
|
112
app/views/layouts/_footer.haml
Normal file
@ -0,0 +1,112 @@
|
||||
%footer.bg-white{"aria-label" => "Site Footer"}
|
||||
.px-4.py-16.mx-auto.sm:px-6.lg:px-8
|
||||
.w-full.grid.grid-cols-2.gap-8.mt-8.lg:mt-0.lg:grid-cols-6.lg:gap-y-16.ld:grid-cols-3.lg:gap-y-8
|
||||
.max-w-screen-xl.px-4.py-16.mx-auto.sm:px-6.lg:px-8
|
||||
.lg:flex.lg:items-start.lg:gap-8
|
||||
%a.inline-flex.items-center{"aria-label" => "Hub Feenix", :href => "/", :title => "Hub Feenix"}
|
||||
= image_tag("feenix_lintu.webp" , class: "h-20")
|
||||
|
||||
%nav.mt-6
|
||||
%p.mb-6.font-medium.text-xl.text-gray-900
|
||||
Activities
|
||||
%ul.space-y-4.text-sm
|
||||
%li
|
||||
%a.text-gray-700.transition.hover:opacity-75{:href => "/local_activities"}
|
||||
Local Area
|
||||
%li
|
||||
%a.text-gray-700.transition.hover:opacity-75{:href => "/helsinki"}
|
||||
Helsinki
|
||||
%li
|
||||
%a.text-gray-700.transition.hover:opacity-75{:href => "/turku"}
|
||||
Turkku
|
||||
|
||||
%nav.mt-6
|
||||
%p.mb-6.font-medium.text-xl.text-gray-900
|
||||
Weather Info
|
||||
%ul.space-y-4.text-sm
|
||||
%li
|
||||
%a.text-md.transition.hover:opacity-75{:href => "/four_seasons"}
|
||||
Four Seasons
|
||||
%li
|
||||
%a.text-gray-700.transition.hover:opacity-75{:href => "/in_summer"}
|
||||
In Summer
|
||||
%li
|
||||
%a.text-gray-700.transition.hover:opacity-75{:href => "/in_winter"}
|
||||
In Winter
|
||||
|
||||
%nav.mt-6{"aria-label" => "Footer Navigation - Hub Feenix"}
|
||||
%p.mb-6.font-medium.text-xl.text-gray-900
|
||||
Volunteering
|
||||
%ul.space-y-4.text-sm
|
||||
%li
|
||||
%a.text-gray-700.transition.hover:opacity-75{:href => "/volunteering"}
|
||||
About Volunteering
|
||||
%li
|
||||
%a.text-gray-700.transition.hover:opacity-75{:href => "/arriving"}
|
||||
Getting here
|
||||
%li
|
||||
%a.text-gray-700.transition.hover:opacity-75{:href => "/living_here"}
|
||||
Living @ Feenix Info
|
||||
|
||||
%nav.mt-6{"aria-label" => "Footer Navigation - Hub Feenix"}
|
||||
%p.mb-6.font-medium.text-xl.text-gray-900
|
||||
People
|
||||
%ul.space-y-4.text-sm
|
||||
%li
|
||||
%a.text-gray-700.transition.hover:opacity-75{:href => "/members"}
|
||||
Makerspace
|
||||
%li
|
||||
%a.text-gray-700.transition.hover:opacity-75{:href => "/stories"}
|
||||
Stories
|
||||
%li
|
||||
%a.text-gray-700.transition.hover:opacity-75{:href => "/pictures"}
|
||||
Gallery
|
||||
|
||||
%nav.mt-6{"aria-label" => "Footer Navigation - Downloads"}
|
||||
%p.mb-6.font-medium.text-xl.text-gray-900
|
||||
Information
|
||||
%ul.space-y-4.text-sm
|
||||
%li
|
||||
%a.text-gray-700.transition.hover:opacity-75{:href => "/about"}
|
||||
About Hub Feenix
|
||||
%li
|
||||
%a.text-gray-700.transition.hover:opacity-75{:href => "https://www.hubfeenix.fi" , target: :blank }
|
||||
Hub Feenix Website
|
||||
%li
|
||||
%a.text-gray-700.transition.hover:opacity-75{:href => "https://www.facebook.com/hubfeenix" , :target => "_blank"}
|
||||
Facebook
|
||||
%li
|
||||
%a.text-gray-700.transition.hover:opacity-75{:href => "https://www.instagram.com/hub_feenix/" , :target => "_blank"}
|
||||
Instagram
|
||||
|
||||
%ul.flex.justify-start.col-span-2.gap-6.lg:col-span-5.lg:justify-end
|
||||
%li
|
||||
%a.text-gray-700.transition.hover:opacity-75{:href => "https://www.facebook.com/hubfeenix", :rel => "noreferrer", :target => "_blank"}
|
||||
%span.sr-only Facebook
|
||||
%svg.w-6.h-6{"aria-hidden" => "true", :fill => "currentColor", :viewBox => "0 0 24 24"}
|
||||
%path{"clip-rule" => "evenodd", :d => "M22 12c0-5.523-4.477-10-10-10S2 6.477 2 12c0 4.991 3.657 9.128 8.438 9.878v-6.987h-2.54V12h2.54V9.797c0-2.506 1.492-3.89 3.777-3.89 1.094 0 2.238.195 2.238.195v2.46h-1.26c-1.243 0-1.63.771-1.63 1.562V12h2.773l-.443 2.89h-2.33v6.988C18.343 21.128 22 16.991 22 12z", "fill-rule" => "evenodd"}
|
||||
%li
|
||||
%a.text-gray-700.transition.hover:opacity-75{:href => "https://www.instagram.com/hub_feenix/", :rel => "noreferrer", :target => "_blank"}
|
||||
%span.sr-only Instagram
|
||||
%svg.w-6.h-6{"aria-hidden" => "true", :fill => "currentColor", :viewBox => "0 0 24 24"}
|
||||
%path{"clip-rule" => "evenodd", :d => "M12.315 2c2.43 0 2.784.013 3.808.06 1.064.049 1.791.218 2.427.465a4.902 4.902 0 011.772 1.153 4.902 4.902 0 011.153 1.772c.247.636.416 1.363.465 2.427.048 1.067.06 1.407.06 4.123v.08c0 2.643-.012 2.987-.06 4.043-.049 1.064-.218 1.791-.465 2.427a4.902 4.902 0 01-1.153 1.772 4.902 4.902 0 01-1.772 1.153c-.636.247-1.363.416-2.427.465-1.067.048-1.407.06-4.123.06h-.08c-2.643 0-2.987-.012-4.043-.06-1.064-.049-1.791-.218-2.427-.465a4.902 4.902 0 01-1.772-1.153 4.902 4.902 0 01-1.153-1.772c-.247-.636-.416-1.363-.465-2.427-.047-1.024-.06-1.379-.06-3.808v-.63c0-2.43.013-2.784.06-3.808.049-1.064.218-1.791.465-2.427a4.902 4.902 0 011.153-1.772A4.902 4.902 0 015.45 2.525c.636-.247 1.363-.416 2.427-.465C8.901 2.013 9.256 2 11.685 2h.63zm-.081 1.802h-.468c-2.456 0-2.784.011-3.807.058-.975.045-1.504.207-1.857.344-.467.182-.8.398-1.15.748-.35.35-.566.683-.748 1.15-.137.353-.3.882-.344 1.857-.047 1.023-.058 1.351-.058 3.807v.468c0 2.456.011 2.784.058 3.807.045.975.207 1.504.344 1.857.182.466.399.8.748 1.15.35.35.683.566 1.15.748.353.137.882.3 1.857.344 1.054.048 1.37.058 4.041.058h.08c2.597 0 2.917-.01 3.96-.058.976-.045 1.505-.207 1.858-.344.466-.182.8-.398 1.15-.748.35-.35.566-.683.748-1.15.137-.353.3-.882.344-1.857.048-1.055.058-1.37.058-4.041v-.08c0-2.597-.01-2.917-.058-3.96-.045-.976-.207-1.505-.344-1.858a3.097 3.097 0 00-.748-1.15 3.098 3.098 0 00-1.15-.748c-.353-.137-.882-.3-1.857-.344-1.023-.047-1.351-.058-3.807-.058zM12 6.865a5.135 5.135 0 110 10.27 5.135 5.135 0 010-10.27zm0 1.802a3.333 3.333 0 100 6.666 3.333 3.333 0 000-6.666zm5.338-3.205a1.2 1.2 0 110 2.4 1.2 1.2 0 010-2.4z", "fill-rule" => "evenodd"}
|
||||
%li
|
||||
%a.text-gray-700.transition.hover:opacity-75{:href => "https://github.com/FeenixMakers", :rel => "noreferrer", :target => "_blank"}
|
||||
%span.sr-only GitHub
|
||||
%svg.w-6.h-6{"aria-hidden" => "true", :fill => "currentColor", :viewBox => "0 0 24 24"}
|
||||
%path{"clip-rule" => "evenodd", :d => "M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z", "fill-rule" => "evenodd"}
|
||||
.pt-8.mt-8.border-t.border-gray-100
|
||||
.grid.grid-cols-1.gap-8.lg:grid-cols-2
|
||||
%p.text-xs.text-left.text-gray-500
|
||||
2020-23. Osuuskunta Hub Feenix. All rights reserved.
|
||||
%nav{"aria-label" => "Footer Navigation - Support"}
|
||||
%ul.flex.flex-wrap.justify-start.gap-4.text-xs.lg:justify-end
|
||||
%li
|
||||
%a.text-gray-500.transition.hover:opacity-75{:href => "#"}
|
||||
=#Terms Conditions
|
||||
%li
|
||||
%a.text-gray-500.transition.hover:opacity-75{:href => "#"}
|
||||
=#Privacy Policy
|
||||
%li
|
||||
%a.text-gray-500.transition.hover:opacity-75{:href => "#"}
|
||||
=#Cookies
|
38
app/views/layouts/_header.haml
Normal file
@ -0,0 +1,38 @@
|
||||
%header.px-4.py-5.mx-auto.sm:max-w-xl.md:max-w-full.lg:max-w-screen-xl.md:px-24.lg:px-8
|
||||
.relative.flex.items-center.justify-between
|
||||
%a.inline-flex.items-center{"aria-label" => "Hub Feenix", :href => "/", :title => "Hub Feenix"}
|
||||
= image_tag("feenix_lintu.webp" , class: "h-20")
|
||||
%span.ml-2.text-xl.font-bold.text-gray-800 Makerspace @ Hub Feenix
|
||||
%ul.flex.items-center.hidden.space-x-8.lg:flex
|
||||
- main_menu.each do |link , text|
|
||||
%li
|
||||
%a.font-medium.tracking-wide.text-gray-700.transition-colors.duration-400.hover:text-cyan-800{"aria-label" => "Our product", :href => link, :title => text}= text
|
||||
%li
|
||||
= link_to "https://www.facebook.com/hubfeenix" , :target => "_blank" do
|
||||
= image_tag("fb.webp" , class: "h-10 rounded-md")
|
||||
%li
|
||||
- unless member_signed_in?
|
||||
%a.inline-flex.items-center.justify-center.h-12.px-6.font-medium.tracking-wide.text-white.transition.duration-200.rounded-lg.shadow-md.bg-green-800.hover:bg-blue-800.focus:shadow-outline.focus:outline-none{"aria-label" => "Sign up", :href => main_app.member_session_path, :title => "Log in or Sign up"}
|
||||
Login
|
||||
- else
|
||||
.inline-flex.items-stretch.rounded-md.border
|
||||
.rounded-l-md.px-4.py-2.text-sm.text-gray-600.hover:bg-cyan-100.hover:text-gray-900
|
||||
=link_to current_member.email , main_app.member_path(current_member)
|
||||
.relative
|
||||
%button.inline-flex.h-full.items-center.justify-center.rounded-r-md.border-l.border-gray-100.px-2.text-gray-600.hover:bg-cyan-400{:type => "button" , onclick: "dropdown();" }
|
||||
%span.sr-only Menu
|
||||
%svg.h-4.w-4{:fill => "currentColor", :viewbox => "0 0 20 20", :xmlns => "http://www.w3.org/2000/svg"}
|
||||
%path{"clip-rule" => "evenodd", :d => "M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z", "fill-rule" => "evenodd"}
|
||||
=render "layouts/member_menu"
|
||||
|
||||
.lg:hidden.flex.items-center.justify-between
|
||||
.mr-20
|
||||
= link_to "https://www.facebook.com/hubfeenix" , :target => "_blank" do
|
||||
= image_tag("fb.webp" , class: "h-10 rounded-md")
|
||||
%button.p-2.-mr-1.transition.duration-200.rounded.focus:outline-none.focus:shadow-outline.hover:bg-deep-purple-50.focus:bg-deep-purple-50{"aria-label" => "Open Menu", :title => "Open Menu" , onclick: "menu_on();"}
|
||||
%svg.w-5.text-gray-600{:viewbox => "0 0 24 24"}
|
||||
%path{:d => "M23,13H1c-0.6,0-1-0.4-1-1s0.4-1,1-1h22c0.6,0,1,0.4,1,1S23.6,13,23,13z", :fill => "currentColor"}
|
||||
%path{:d => "M23,6H1C0.4,6,0,5.6,0,5s0.4-1,1-1h22c0.6,0,1,0.4,1,1S23.6,6,23,6z", :fill => "currentColor"}
|
||||
%path{:d => "M23,20H1c-0.6,0-1-0.4-1-1s0.4-1,1-1h22c0.6,0,1,0.4,1,1S23.6,20,23,20z", :fill => "currentColor"}
|
||||
|
||||
= render "layouts/mobile_menu"
|
23
app/views/layouts/_member_menu.haml
Normal file
@ -0,0 +1,23 @@
|
||||
#menu-dropdown.hidden.absolute.right-0.z-10.mt-4.w-36.origin-top-right.rounded-md.border.border-gray-100.bg-white.shadow-lg{:role => "menu"}
|
||||
.p-2
|
||||
- member_memu.each do |link , text|
|
||||
%a.block.rounded-lg.px-4.py-2.text-sm.text-gray-500.hover:bg-gray-50.hover:text-gray-700{:href => link, :role => "menuitem"}
|
||||
=text
|
||||
= form_tag( main_app.destroy_member_session_path , {method: :delete } ) do
|
||||
%button.flex.w-full.items-center.gap-2.rounded-lg.px-4.py-2.text-sm.text-blue-700.hover:bg-red-50{:role => "menuitem", :type => "submit"}
|
||||
%svg.h-4.w-4{:fill => "none", :stroke => "currentColor", "stroke-width" => "2", :viewbox => "0 0 24 24", :xmlns => "http://www.w3.org/2000/svg"}
|
||||
%path{:d => "M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16", "stroke-linecap" => "round", "stroke-linejoin" => "round"}
|
||||
Sign out
|
||||
|
||||
:javascript
|
||||
var drop_hidden = true;
|
||||
function dropdown(){
|
||||
var x = document.getElementById("menu-dropdown");
|
||||
if( drop_hidden == true){
|
||||
x.style.display = "block";
|
||||
drop_hidden = false ;
|
||||
} else {
|
||||
x.style.display = "none";
|
||||
drop_hidden = true ;
|
||||
}
|
||||
}
|
28
app/views/layouts/_messages.haml
Normal file
@ -0,0 +1,28 @@
|
||||
-if flash.alert
|
||||
#flash
|
||||
.m-20.rounded.border-l-4.border-red-500.bg-red-50.p-4{:role => "alert"}
|
||||
%strong.block.font-medium.text-red-700 Oops
|
||||
%p.mt-2.text-sm.text-red-700
|
||||
=flash.alert
|
||||
|
||||
-if flash.notice
|
||||
#flash
|
||||
.m-20.rounded-xl.border.border-gray-100.p-4.shadow-xl{:role => "alert"}
|
||||
.flex.items-start.gap-4
|
||||
%span.text-green-600
|
||||
%svg.h-6.w-6{:fill => "none", :stroke => "currentColor", "stroke-width" => "1.5", :viewbox => "0 0 24 24", :xmlns => "http://www.w3.org/2000/svg"}
|
||||
%path{:d => "M9 12.75L11.25 15 15 9.75M21 12a9 9 0 11-18 0 9 9 0 0118 0z", "stroke-linecap" => "round", "stroke-linejoin" => "round"}
|
||||
.flex-1
|
||||
%strong.block.font-medium.text-gray-900 Ok
|
||||
%p.mt-1.text-sm.text-gray-700
|
||||
= flash.notice
|
||||
:javascript
|
||||
function hideNotice() {
|
||||
const notification = document.querySelector('#flash')
|
||||
if (notification) {
|
||||
setInterval(function() {
|
||||
notification.classList.add('hidden');
|
||||
}, 5000);
|
||||
}
|
||||
}
|
||||
hideNotice();
|
42
app/views/layouts/_mobile_menu.haml
Normal file
@ -0,0 +1,42 @@
|
||||
#mobile-menu.absolute.top-0.leaving-0.w-full.hidden.z-10
|
||||
.p-5.bg-white.border.rounded.shadow-sm
|
||||
.flex.items-center.justify-between.mb-4
|
||||
%div
|
||||
%a.inline-flex.items-center{"aria-label" => "Hub Feenix", :href => "/", :title => "Hub Feenix"}
|
||||
= image_tag("feenix_lintu.webp" , class: "h-20")
|
||||
%span.ml-2.text-xl.font-bold.tracking-wide.text-gray-800.uppercase Hub Feenix
|
||||
%div
|
||||
%button.p-2.-mt-2.-mr-2.transition.duration-200.rounded.hover:bg-gray-200.focus:bg-gray-200.focus:outline-none.focus:shadow-outline{"aria-label" => "Close Menu", :title => "Close Menu" , onclick: "menu_off();"}
|
||||
%svg.w-5.text-gray-600{:viewbox => "0 0 24 24"}
|
||||
%path{:d => "M19.7,4.3c-0.4-0.4-1-0.4-1.4,0L12,10.6L5.7,4.3c-0.4-0.4-1-0.4-1.4,0s-0.4,1,0,1.4l6.3,6.3l-6.3,6.3 c-0.4,0.4-0.4,1,0,1.4C4.5,19.9,4.7,20,5,20s0.5-0.1,0.7-0.3l6.3-6.3l6.3,6.3c0.2,0.2,0.5,0.3,0.7,0.3s0.5-0.1,0.7-0.3 c0.4-0.4,0.4-1,0-1.4L13.4,12l6.3-6.3C20.1,5.3,20.1,4.7,19.7,4.3z", :fill => "currentColor"}
|
||||
%nav
|
||||
%ul.space-y-6.px-20.w-lg.text-center
|
||||
- main_menu.each do |link , text|
|
||||
%li
|
||||
%a.w-full.h-full.block.tracking-wide.text-xl.p-2.rounded-lg.hover:bg-cyan-200{ :href => link , :title => text}
|
||||
= text
|
||||
%li
|
||||
%hr
|
||||
- mobile_menu.each do |link , text|
|
||||
%li
|
||||
%a.w-full.h-full.block.tracking-wide.text-xl.p-2.rounded-lg.hover:bg-cyan-200{ :href => link , :title => text}
|
||||
= text
|
||||
-if(current_member)
|
||||
= form_tag( main_app.destroy_member_session_path , {method: :delete ,class: "text-center"}) do
|
||||
%button.w-full.items-center.gap-2.rounded-lg.px-4.py-2.text-sm.text-blue-700.hover:bg-red-50{:role => "menuitem", :type => "submit"}
|
||||
%svg.h-4.w-4{:fill => "none", :stroke => "currentColor", "stroke-width" => "2", :viewbox => "0 0 24 24", :xmlns => "http://www.w3.org/2000/svg"}
|
||||
%path{:d => "M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16", "stroke-linecap" => "round", "stroke-linejoin" => "round"}
|
||||
Sign out
|
||||
-else
|
||||
%a.inline-flex.items-center.justify-center.h-12.px-6.font-medium.tracking-wide.text-white.transition.duration-200.rounded-lg.shadow-md.bg-green-800.hover:bg-blue-800.focus:shadow-outline.focus:outline-none{"aria-label" => "Sign up", :href => main_app.member_session_path, :title => "Log in or Sign up"}
|
||||
Login
|
||||
:javascript
|
||||
var drop_hidden = true;
|
||||
function menu_on() {
|
||||
var x = document.getElementById("mobile-menu");
|
||||
x.style.display = "block";
|
||||
}
|
||||
function menu_off() {
|
||||
var x = document.getElementById("mobile-menu");
|
||||
x.style.display = "none";
|
||||
}
|
39
app/views/layouts/application.haml
Normal file
@ -0,0 +1,39 @@
|
||||
!!!
|
||||
%html
|
||||
%head
|
||||
%meta{:content => "text/html; charset=UTF-8", "http-equiv" => "Content-Type"}/
|
||||
%title Hubfeenix Makerspace
|
||||
%meta{:content => "width=device-width,initial-scale=1", :name => "viewport"}/
|
||||
// https://www.favicon-generator.org/
|
||||
%link{:href => "/apple-icon-57x57.png", :rel => "apple-touch-icon", :sizes => "57x57"}/
|
||||
%link{:href => "/apple-icon-60x60.png", :rel => "apple-touch-icon", :sizes => "60x60"}/
|
||||
%link{:href => "/apple-icon-72x72.png", :rel => "apple-touch-icon", :sizes => "72x72"}/
|
||||
%link{:href => "/apple-icon-76x76.png", :rel => "apple-touch-icon", :sizes => "76x76"}/
|
||||
%link{:href => "/apple-icon-114x114.png", :rel => "apple-touch-icon", :sizes => "114x114"}/
|
||||
%link{:href => "/apple-icon-120x120.png", :rel => "apple-touch-icon", :sizes => "120x120"}/
|
||||
%link{:href => "/apple-icon-144x144.png", :rel => "apple-touch-icon", :sizes => "144x144"}/
|
||||
%link{:href => "/apple-icon-152x152.png", :rel => "apple-touch-icon", :sizes => "152x152"}/
|
||||
%link{:href => "/apple-icon-180x180.png", :rel => "apple-touch-icon", :sizes => "180x180"}/
|
||||
%link{:href => "/android-icon-192x192.png", :rel => "icon", :sizes => "192x192", :type => "image/png"}/
|
||||
%link{:href => "/favicon-32x32.png", :rel => "icon", :sizes => "32x32", :type => "image/png"}/
|
||||
%link{:href => "/favicon-96x96.png", :rel => "icon", :sizes => "96x96", :type => "image/png"}/
|
||||
%link{:href => "/favicon-16x16.png", :rel => "icon", :sizes => "16x16", :type => "image/png"}/
|
||||
%link{:href => "/manifest.json", :rel => "manifest"}/
|
||||
%meta{:content => "#ffffff", :name => "msapplication-TileColor"}/
|
||||
%meta{:content => "/ms-icon-144x144.png", :name => "msapplication-TileImage"}/
|
||||
%meta{:content => "#ffffff", :name => "theme-color"}/
|
||||
|
||||
= csrf_meta_tags
|
||||
= csp_meta_tag
|
||||
= stylesheet_link_tag "tailwind"
|
||||
= stylesheet_link_tag "application"
|
||||
|
||||
= javascript_importmap_tags
|
||||
- if false
|
||||
%script{:src => "https://cdn.tailwindcss.com"}
|
||||
|
||||
%body.xl:mx-auto{class: "max-w-[1920px]"}
|
||||
= render "layouts/header"
|
||||
= render "layouts/messages"
|
||||
= yield
|
||||
= render "layouts/footer"
|
8
app/views/layouts/mailer.html.haml
Normal file
@ -0,0 +1,8 @@
|
||||
!!!
|
||||
%html
|
||||
%head
|
||||
%meta{:content => "text/html; charset=utf-8", "http-equiv" => "Content-Type"}/
|
||||
:css
|
||||
/* Email styles need to be inline */
|
||||
%body
|
||||
= yield
|
1
app/views/layouts/mailer.text.haml
Normal file
@ -0,0 +1 @@
|
||||
= yield
|
23
app/views/layouts/thredded_layout.haml
Normal file
@ -0,0 +1,23 @@
|
||||
!!!
|
||||
%html
|
||||
%head
|
||||
%meta{:content => "text/html; charset=UTF-8", "http-equiv" => "Content-Type"}/
|
||||
%title
|
||||
Forum #{yield :thredded_page_title}
|
||||
%meta{:content => "width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0", :name => "viewport"}/
|
||||
= stylesheet_link_tag "tailwind"
|
||||
= stylesheet_link_tag 'thredded', 'data-turbolinks-track': 'reload'
|
||||
= stylesheet_link_tag "application"
|
||||
= csrf_meta_tag
|
||||
= csp_meta_tag
|
||||
= javascript_include_tag 'thredded', |
|
||||
async: !Rails.application.config.assets.debug, |
|
||||
defer: true, |
|
||||
'data-turbolinks-track': 'reload' |
|
||||
= RailsGravatar.prefetch_dns_tag
|
||||
%meta{:content => "width=device-width, initial-scale=1, user-scalable=no", :name => "viewport"}/
|
||||
%body
|
||||
= render "layouts/header"
|
||||
= render "layouts/messages"
|
||||
= yield
|
||||
= render "layouts/footer"
|
26
app/views/members/edit.html.haml
Normal file
@ -0,0 +1,26 @@
|
||||
%script{:src => "https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"}
|
||||
%script{:src => "https://cdn.jsdelivr.net/npm/marked/marked.min.js"}
|
||||
|
||||
.flex.justify-center.m-5.m-5.md:m-12.lg:m-20
|
||||
.flex.flex-col.text-center
|
||||
%h1.text-4xl Edit your profile
|
||||
|
||||
.flex.justify-center.m-5.m-5.md:m-12.lg:m-20
|
||||
.flex.flex-col.text-center{class: "w-full md:w-10/12"}
|
||||
The Picture box is landscape with ratio 3/4.
|
||||
= simple_form_for @member do |f|
|
||||
.grid.grid-cols-1.md:grid-cols-2.gap-10
|
||||
= render "merged/form/editor" , object: @member , field: :bio, form: f
|
||||
.info.mr-8
|
||||
.text-red-700= f.error_notification
|
||||
= f.input :name
|
||||
.flex.h-16.mt-2.col-span-2
|
||||
= image_tag(@member.picture_url , class: "align-middle mr-4") if @member.picture?
|
||||
.w-full= f.input :picture , as: :file ,
|
||||
label: (@member.picture.blank? ? "Add picture" : "Change picture")
|
||||
= f.hidden_field :picture_cache
|
||||
.flex.justify-center.actions.m-10
|
||||
= f.button :button, "Update", class: button_classes + " bg-cyan-200"
|
||||
= link_to member_path(@member) do
|
||||
%button.ml-10{type: :submit, class: button_classes}
|
||||
Back
|
20
app/views/members/index.html.haml
Normal file
@ -0,0 +1,20 @@
|
||||
= paginate @members
|
||||
|
||||
.flex.justify-center.m-8.mx-5.md:mx-12.lg:mx-20
|
||||
.flex.flex-col.text-center
|
||||
%h1.text-4xl Current Makerspace at Hub Feenix
|
||||
|
||||
.mx-20.grid.grid-cols-1.md:grid-cols-2.lg:grid-cols-3.2xl:grid-cols-4.gap-8.md:gap-12.lg:gap-16
|
||||
- @members.each do |member|
|
||||
.overflow-hidden.border.border-gray-100.shadow-sm
|
||||
.h-0.overflow-hidden.relative{class: "pt-[125%]"}
|
||||
=link_to member , class: "absolute h-60 top-0 left-0 w-full h-full" do
|
||||
= picture_for( member , class: "object-cover")
|
||||
%h3.pt-5.text-2xl.bg-gray-100.text-black.font-bold.text-center
|
||||
= member.name
|
||||
.p-2.text-xs.bg-gray-50.text-black.font-bold.text-center
|
||||
= member.name
|
||||
%div.h-full
|
||||
.p-5.text-center
|
||||
.m-2.text-sm.leading-relaxed.line-clamp-3{ prose_classes }
|
||||
= markdown shorten(member.bio)
|
57
app/views/members/show.html.haml
Normal file
@ -0,0 +1,57 @@
|
||||
.px-4.py-16.mx-auto.sm:max-w-xl.md:max-w-full.lg:max-w-screen-xl.md:px-24.lg:px-8.lg:py-20
|
||||
.flex.flex-col.max-w-screen-lg.overflow-hidden.bg-white.border.rounded.shadow-sm.lg:flex-row.sm:mx-auto
|
||||
.relative{:class => "lg:w-1/2"}
|
||||
-if @member.picture_url
|
||||
= image_tag @member.picture_url, class: "object-cover w-full lg:absolute h-80 lg:h-full"
|
||||
.flex.flex-col.justify-center.p-8.lg:p-16.lg:pl-10{:class => "lg:w-1/2"}
|
||||
%div
|
||||
%p.inline-block.px-3.py-px.mb-4.text-xs.font-semibold.tracking-wider.text-teal-900.uppercase.rounded-full.bg-teal-accent-400
|
||||
= @member.name
|
||||
%h5.mb-3.text-3xl.font-extrabold.leading-none.sm:text-4xl
|
||||
= @member.name
|
||||
.mb-8.text-gray-800
|
||||
.prose= markdown(@member.bio)
|
||||
|
||||
- if current_member == @member
|
||||
.flex.justify-around.ml-20
|
||||
= link_to edit_member_path(@member) do
|
||||
%button.bg-cyan-200.mr-3.inline-block.rounded-lg.px-4.py-3.text-md.font-medium.border.border-gray-400
|
||||
Edit Profile
|
||||
|
||||
= link_to edit_member_registration_path do
|
||||
%button.bg-cyan-200.mr-3.inline-block.rounded-lg.px-4.py-3.text-md.font-medium.border.border-gray-400
|
||||
Change Password
|
||||
|
||||
= link_to new_story_path() do
|
||||
%button.bg-cyan-200.mr-3.inline-block.rounded-lg.px-4.py-3.text-md.font-medium.border.border-gray-400
|
||||
New Story
|
||||
|
||||
= link_to new_picture_path() do
|
||||
%button.bg-cyan-200.mr-3.inline-block.rounded-lg.px-4.py-3.text-md.font-medium.border.border-gray-400
|
||||
New Picture
|
||||
|
||||
= form_tag( destroy_member_session_path , {method: :delete } ) do
|
||||
%button.bg-cyan-200.mr-3.inline-block.rounded-lg.px-4.py-3.text-md.font-medium.border.border-gray-400{type: :submit}
|
||||
Sign out
|
||||
|
||||
- @member.stories.each do |story|
|
||||
=render_story( story )
|
||||
- if current_member == @member
|
||||
.flex.justify-around
|
||||
= link_to edit_story_path(story) do
|
||||
%button.bg-cyan-200.mr-3.inline-block.rounded-lg.px-4.py-3.text-md.font-medium.border.border-gray-400
|
||||
Edit Story
|
||||
|
||||
.mx-20.grid.grid-cols-1.md:grid-cols-2.lg:grid-cols-3.xl:grid-cols-4.gap-8.md:gap-12.lg:gap-16
|
||||
- @member.pictures.each do |picture|
|
||||
%div
|
||||
= render picture , picture: picture
|
||||
- if current_member == @member
|
||||
.flex.justify-around
|
||||
= link_to edit_picture_path(picture) do
|
||||
%button.bg-cyan-200.mr-3.inline-block.rounded-lg.px-4.py-3.text-md.font-medium.border.border-gray-400
|
||||
Edit Picture
|
||||
:javascript
|
||||
document.addEventListener("DOMContentLoaded", function(event) {
|
||||
const lightbox = GLightbox({ });
|
||||
});
|
8
app/views/merged/view/_section_pictures.haml
Normal file
@ -0,0 +1,8 @@
|
||||
.mx-20.grid.grid-cols-1.md:grid-cols-2.lg:grid-cols-3.2xl:grid-cols-4.gap-8.md:gap-12.lg:gap-16
|
||||
- Picture.all.limit(4).each do |picture|
|
||||
= render "pictures/picture" , picture: picture
|
||||
|
||||
:javascript
|
||||
document.addEventListener("DOMContentLoaded", function(event) {
|
||||
const lightbox = GLightbox({ });
|
||||
});
|
2
app/views/merged/view/_section_story.haml
Normal file
@ -0,0 +1,2 @@
|
||||
- story = Story.last
|
||||
= render_story story
|
18
app/views/pictures/_form.haml
Normal file
@ -0,0 +1,18 @@
|
||||
%div
|
||||
Pictures may have a small text, that will be diplayed on top of the
|
||||
picure. The Picture box is landscape with ratio 3/4.
|
||||
= simple_form_for @picture do |f|
|
||||
= f.error_notification
|
||||
.flex.h-16.mt-2
|
||||
= image_tag(@picture.picture_url , class: "align-middle mr-2") if @picture.picture?
|
||||
.w-full= f.input :picture , as: :file ,
|
||||
label: (@picture.picture.blank? ? "Add picture" : "Change picture")
|
||||
= f.hidden_field :picture_cache
|
||||
= f.input :text , as: :text , input_html: { rows: 2 }
|
||||
= f.input :happened , wrapper_class: "flex mt-4 align-center"
|
||||
.flex.justify-between.mt-6
|
||||
%button.bg-cyan-200.rounded-lg.px-4.py-3.text-md.font-medium.border.border-gray-400
|
||||
= f.submit 'Save'
|
||||
= link_to member_path(current_member) do
|
||||
%button.ml-20.rounded-lg.px-4.py-3.text-md.font-medium.border.border-gray-400
|
||||
Back
|
13
app/views/pictures/_picture.haml
Normal file
@ -0,0 +1,13 @@
|
||||
.group.relative.overflow-hidden
|
||||
.flex.justify-between
|
||||
.ml-2= link_to picture.member.name , main_app.member_path(picture.member) , class: :underline
|
||||
.mr-2
|
||||
= distance_of_time_in_words_to_now picture.happened
|
||||
ago
|
||||
.h-0.overflow-hidden.relative{class: "pt-[75%]"}
|
||||
=link_to someones_path( picture ) , {class: :glightbox ,
|
||||
"data-glightbox" => "title: #{picture.text}" } do
|
||||
= picture_for( picture , "absolute top-0 left-0 w-full h-full inset-0 object-cover hover:scale-105 ease-in duration-500")
|
||||
-unless picture.text.blank?
|
||||
.absolute.bottom-0.leaving-0.right-0.px-4.pb-1.bg-gray-800.opacity-70.transition-colors.group-hover:bg-black.group-hover:opacity-100
|
||||
.text-center.mt-2.text-white= picture.text
|
7
app/views/pictures/edit.html.haml
Normal file
@ -0,0 +1,7 @@
|
||||
%script{:src => "https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"}
|
||||
%script{:src => "https://cdn.jsdelivr.net/npm/marked/marked.min.js"}
|
||||
.flex.justify-center
|
||||
.column{class: "w-10/12 md:w-8/12 lg:w-7/12 xl:w-6/12"}
|
||||
.text-2xl.font-bold.my-4
|
||||
Edit Picture
|
||||
= render 'form'
|
21
app/views/pictures/index.html.haml
Normal file
@ -0,0 +1,21 @@
|
||||
|
||||
= paginate @pictures
|
||||
|
||||
.flex.justify-end.mr-10
|
||||
= sort_link(@q, :happened ,class: 'flex flex-nowrap text-md')
|
||||
.border-r-4.mx-4
|
||||
= sort_link(@q, :created_at , class: 'flex flex-nowrap text-md')
|
||||
|
||||
.mx-20.grid.grid-cols-1.md:grid-cols-2.lg:grid-cols-3.2xl:grid-cols-4.gap-8.md:gap-12.lg:gap-16
|
||||
- @pictures.each do |picture|
|
||||
= render picture , picture: picture
|
||||
|
||||
.flex.ml-20
|
||||
= link_to new_picture_path do
|
||||
%button.bg-cyan-200.mr-3.inline-block.rounded-lg.px-4.py-3.text-md.font-medium.border.border-gray-400
|
||||
New Picture
|
||||
|
||||
:javascript
|
||||
document.addEventListener("DOMContentLoaded", function(event) {
|
||||
const lightbox = GLightbox({ });
|
||||
});
|
7
app/views/pictures/new.html.haml
Normal file
@ -0,0 +1,7 @@
|
||||
%script{:src => "https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"}
|
||||
%script{:src => "https://cdn.jsdelivr.net/npm/marked/marked.min.js"}
|
||||
.flex.justify-center
|
||||
.column{class: "w-10/12 md:w-8/12 lg:w-7/12 xl:w-6/12"}
|
||||
.text-2xl.font-bold.my-4
|
||||
New Picture
|
||||
= render 'form'
|
11
app/views/pictures/show.html.haml
Normal file
@ -0,0 +1,11 @@
|
||||
.grid.grid-cols-1.md:grid-cols-2.lg:grid-cols-3.gap-8.md:gap-12.lg:gap-16
|
||||
%div
|
||||
= render @picture , picture: @picture
|
||||
|
||||
.mx-20.flex.justify-between
|
||||
= link_to edit_picture_path(@picture) do
|
||||
%button.mt-6.bg-cyan-200.mr-3.inline-block.rounded-lg.px-4.py-3.text-md.font-medium.border.border-gray-400
|
||||
Edit
|
||||
= link_to pictures_path do
|
||||
%button.mt-6.mr-3.inline-block.rounded-lg.px-4.py-3.text-md.font-medium.border.border-gray-400
|
||||
Back
|
20
app/views/stories/_form.haml
Normal file
@ -0,0 +1,20 @@
|
||||
%div
|
||||
Story layout changes with the amount of text.
|
||||
For short text a wide picture is best. Otherwise square, and for
|
||||
longer text a high picture also works.
|
||||
= simple_form_for @story do |f|
|
||||
= f.error_notification
|
||||
.flex.h-16.mt-2
|
||||
= image_tag(@story.picture_url , class: "align-middle mr-2") if @story.picture?
|
||||
.w-full= f.input :picture , as: :file ,
|
||||
label: (@story.picture.blank? ? "Add picture" : "Change picture")
|
||||
= f.hidden_field :picture_cache
|
||||
= f.input :header
|
||||
= render "merged/form/editor" , object: @story , field: :text, form: f
|
||||
= f.input :happened , wrapper_class: "flex mt-4 align-center"
|
||||
.flex.justify-between.mt-6
|
||||
%button.bg-cyan-200.rounded-lg.px-4.py-3.text-md.font-medium.border.border-gray-400
|
||||
= f.submit 'Save'
|
||||
= link_to member_path(current_member) do
|
||||
%button.ml-20.rounded-lg.px-4.py-3.text-md.font-medium.border.border-gray-400
|
||||
Back
|
13
app/views/stories/_half.haml
Normal file
@ -0,0 +1,13 @@
|
||||
%section.overflow-hidden.grid.grid-cols-1.m-5.md:m-12.lg:m-20.md:grid-cols-2
|
||||
=link_to someones_path( story ) , {class: :glightbox } do
|
||||
= picture_for( story , "h-56 w-full object-cover sm:h-full")
|
||||
.p-8.md:p-12.lg:px-16.lg:py-24
|
||||
.mx-auto.max-w-xl.text-center
|
||||
%h2.text-2xl.font-bold.md:text-4xl
|
||||
= story.header
|
||||
%h4.text-xl.mt-10.md:text-2xl
|
||||
= distance_of_time_in_words_to_now story.happened
|
||||
ago, by
|
||||
= link_to story.member.name , main_app.member_path(story.member) , class: :underline
|
||||
.mt-8{ prose_classes }
|
||||
= markdown(story.text)
|
15
app/views/stories/_pic.haml
Normal file
@ -0,0 +1,15 @@
|
||||
%section.flex.justify-center.p-8.flex-col.md:flex-row.m-20
|
||||
.flex.items-center.h-40.md:h-60.lg:h-96.w-full.overflow-hidden{class: "lg:w-2/3"}
|
||||
=link_to someones_path( story ) , {class: :glightbox } do
|
||||
= picture_for(story ,"object-cover")
|
||||
.flex.items-center.w-full.max-w.px-6.mt-6.mx-auto{:class => "lg:w-1/3"}
|
||||
.flex-1
|
||||
.text-center
|
||||
%h2.text-4xl.font-bold.text-center.mb-4.lg:mb-8
|
||||
= story.header
|
||||
%h4.text-xl.mt-4.lg:mt-8.md:text-2xl
|
||||
= distance_of_time_in_words_to_now story.happened
|
||||
ago, by
|
||||
= link_to story.member.name , main_app.member_path(story.member) , class: :underline
|
||||
.mt-3{ prose_classes }
|
||||
= markdown(story.text)
|
13
app/views/stories/_text.haml
Normal file
@ -0,0 +1,13 @@
|
||||
%section.p-20.my-20
|
||||
.flex.justify-center
|
||||
.max-w-prose.text-center
|
||||
%h1.mb-10.text-2xl.font-bold.tracking-tight.sm:text-4xl
|
||||
= story.header
|
||||
.text-xl.pb-6{ prose_classes }
|
||||
= distance_of_time_in_words_to_now story.happened
|
||||
ago, by
|
||||
= link_to story.member.name , main_app.member_path(story.member) , class: :underline
|
||||
.max-w-full.mt-4.gap-16.columns-1.md:columns-2.lg:columns-3.xl:columns-4{ prose_classes }
|
||||
=link_to someones_path( story ) , {class: :glightbox } do
|
||||
= picture_for( story , "h-56 w-full object-cover sm:h-full")
|
||||
= markdown(story.text)
|
7
app/views/stories/edit.html.haml
Normal file
@ -0,0 +1,7 @@
|
||||
%script{:src => "https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"}
|
||||
%script{:src => "https://cdn.jsdelivr.net/npm/marked/marked.min.js"}
|
||||
.flex.justify-center
|
||||
.column{class: "w-10/12 md:w-8/12 lg:w-7/12 xl:w-6/12"}
|
||||
.text-2xl.font-bold.my-4
|
||||
Edit Story
|
||||
= render 'form'
|
21
app/views/stories/index.html.haml
Normal file
@ -0,0 +1,21 @@
|
||||
= paginate @stories
|
||||
|
||||
.flex.justify-end.mr-10
|
||||
= sort_link(@q, :happened ,class: 'flex flex-nowrap text-md')
|
||||
.border-r-4.mx-4
|
||||
= sort_link(@q, :created_at , class: 'flex flex-nowrap text-md')
|
||||
|
||||
- @stories.each do |story|
|
||||
= render_story(story)
|
||||
|
||||
%br
|
||||
|
||||
.flex.ml-20
|
||||
= link_to new_story_path do
|
||||
%button.bg-cyan-200.mr-3.inline-block.rounded-lg.px-4.py-3.text-md.font-medium.border.border-gray-400
|
||||
New Story
|
||||
|
||||
:javascript
|
||||
document.addEventListener("DOMContentLoaded", function(event) {
|
||||
const lightbox = GLightbox({ });
|
||||
});
|
7
app/views/stories/new.html.haml
Normal file
@ -0,0 +1,7 @@
|
||||
%script{:src => "https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"}
|
||||
%script{:src => "https://cdn.jsdelivr.net/npm/marked/marked.min.js"}
|
||||
.flex.justify-center
|
||||
.column{class: "w-10/12 md:w-8/12 lg:w-7/12 xl:w-6/12"}
|
||||
.text-2xl.font-bold.my-4
|
||||
New Story
|
||||
= render 'form'
|
9
app/views/stories/show.html.haml
Normal file
@ -0,0 +1,9 @@
|
||||
= render_story @story
|
||||
|
||||
.mx-20.flex.justify-between
|
||||
= link_to edit_story_path(@story) do
|
||||
%button.mt-6.bg-cyan-200.mr-3.inline-block.rounded-lg.px-4.py-3.text-md.font-medium.border.border-gray-400
|
||||
Edit
|
||||
= link_to stories_path do
|
||||
%button.mt-6.mr-3.inline-block.rounded-lg.px-4.py-3.text-md.font-medium.border.border-gray-400
|
||||
Back
|