diff --git a/Gemfile b/Gemfile index 0d9c045..1967618 100644 --- a/Gemfile +++ b/Gemfile @@ -16,10 +16,11 @@ gem 'html2haml' gem 'devise' gem 'carrierwave', '>= 3.0.0.beta', '< 4.0' +gem 'kaminari' gem "ruby2js" , git: "https://github.com/ruby2js/ruby2js/" , branch: "haml_fix" gem 'thredded', '~> 1.0' gem "rest-client" - +gem "pundit" gem "simple_form" , "5.1.0" gem "simple_form_tailwind_css" diff --git a/Gemfile.lock b/Gemfile.lock index 797a7aa..cfd0187 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -439,10 +439,12 @@ DEPENDENCIES haml-rails html2haml importmap-rails + kaminari merged! mina passenger pg (~> 1.1) + pundit rails (~> 7.0) rest-client ruby2js! diff --git a/app/assets/images/no_image.png b/app/assets/images/no_image.png new file mode 100644 index 0000000..ef843ce Binary files /dev/null and b/app/assets/images/no_image.png differ diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index b13688d..a2779bf 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,9 +1,13 @@ class ApplicationController < ActionController::Base - before_action :configure_permitted_parameters, if: :devise_controller? - protected + include Pundit::Authorization + alias :current_user :current_member #for pundit + rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized - def configure_permitted_parameters - devise_parameter_sanitizer.permit(:sign_up, keys: [:name]) - end + protected + + def user_not_authorized + flash[:alert] = "You are not authorized to perform this action." + redirect_back(fallback_location: root_path) + end end diff --git a/app/controllers/events_controller.rb b/app/controllers/events_controller.rb new file mode 100644 index 0000000..56287e2 --- /dev/null +++ b/app/controllers/events_controller.rb @@ -0,0 +1,65 @@ +class EventsController < ApplicationController + before_action :set_event, only: %i[ show edit update destroy ] + + # GET /events + def index + @events = Event.all + end + + # GET /events/1 + def show + end + + # GET /events/new + def new + kind = params[:profile] + kind = Profile.types.first unless Profile.types.include?(kind) + profile = current_member.profile(kind) + if(profile) + @event = Event.new profile: profile + else + redirect_to member_path(current_member), notice: "No such profile #{kind}." + end + end + + # GET /events/1/edit + def edit + end + + # POST /events + def create + @event = Event.new(event_params) + + if @event.save + redirect_to @event, notice: "Event was successfully created." + else + render :new, status: :unprocessable_entity + end + end + + # PATCH/PUT /events/1 + def update + if @event.update(event_params) + redirect_to @event, notice: "Event was successfully updated." + else + render :edit, status: :unprocessable_entity + end + end + + # DELETE /events/1 + def destroy + @event.destroy + redirect_to events_url, notice: "Event was successfully destroyed." + end + + private + # Use callbacks to share common setup or constraints between actions. + def set_event + @event = Event.find(params[:id]) + end + + # Only allow a list of trusted parameters through. + def event_params + params.require(:event).permit(:name, :text, :start_date, :end_date, :profile_id, :picture) + end +end diff --git a/app/controllers/members_controller.rb b/app/controllers/members_controller.rb index ce47e25..df467b7 100644 --- a/app/controllers/members_controller.rb +++ b/app/controllers/members_controller.rb @@ -3,7 +3,7 @@ class MembersController < ApplicationController # GET /members def index - @members = Member.all + @members = Member.page params[:page] end # GET /members/1 diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb new file mode 100644 index 0000000..bd39d82 --- /dev/null +++ b/app/controllers/profiles_controller.rb @@ -0,0 +1,64 @@ +class ProfilesController < ApplicationController + before_action :set_profile, only: %i[ show edit update destroy ] + + # GET /profiles + def index + @profiles = Profile.page params[:page] + end + + # GET /profiles/1 + def show + end + + # GET /profiles/new + def new + kind = params[:kind] + kind = Profile.types.first unless Profile.types.include?(kind) + @profile = Profile.new kind: kind + end + + # GET /profiles/1/edit + def edit + authorize @profile + end + + # POST /profiles + def create + @profile = Profile.new(profile_params) + @profile.member = current_member + + if @profile.save + redirect_to member_path(current_member), notice: "Successfully created #{@profile.Kind} profile" + else + render :new, status: :unprocessable_entity + end + end + + # PATCH/PUT /profiles/1 + def update + authorize @profile + if @profile.update(profile_params) + redirect_to member_path(current_member), notice: "#{@profile.Kind} profile was updated." + else + render :edit, status: :unprocessable_entity + end + end + + # DELETE /profiles/1 + def destroy + authorize @profile + @profile.destroy + redirect_to member_path(current_member), notice: "#{@profile.Kind} profile was successfully destroyed." + end + + private + # Use callbacks to share common setup or constraints between actions. + def set_profile + @profile = Profile.find(params[:id]) + end + + # Only allow a list of trusted parameters through. + def profile_params + params.require(:profile).permit(:name, :bio, :picture, :kind) + end +end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index df5818d..f839217 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -1,21 +1,12 @@ -require "redcarpet" module ApplicationHelper + include Merged::SharedHelper - 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) + def shorten(text , to = 100) + return "" if text.blank? + "#{text[0..to]} . . . ".html_safe 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 main_app Rails.application.routes.url_helpers @@ -25,4 +16,13 @@ module ApplicationHelper "mr-3 inline-block rounded-lg px-3 py-2 text-md font-medium border border-gray-500" end + def image_for(someone , classes = "") + if someone.picture.blank? + image = asset_url("no_image.png") + else + image = someone.picture.url + end + image_tag(image , alt: someone.name , class: classes ) + end + end diff --git a/app/helpers/events_helper.rb b/app/helpers/events_helper.rb new file mode 100644 index 0000000..8a9a878 --- /dev/null +++ b/app/helpers/events_helper.rb @@ -0,0 +1,2 @@ +module EventsHelper +end diff --git a/app/helpers/profiles_helper.rb b/app/helpers/profiles_helper.rb new file mode 100644 index 0000000..4e43050 --- /dev/null +++ b/app/helpers/profiles_helper.rb @@ -0,0 +1,2 @@ +module ProfilesHelper +end diff --git a/app/models/entity.rb b/app/models/entity.rb index 6d5cea5..4a2aa0c 100644 --- a/app/models/entity.rb +++ b/app/models/entity.rb @@ -20,8 +20,8 @@ class Entity < ApplicationRecord end def self.response(path = "") - host = Rails.credentials.weather_dev - host = Rails.credentials.weather_pro if Rails.env.production? + host = Rails.application.credentials.weather_dev + host = Rails.application.credentials.weather_pro if Rails.env.production? token = Rails.application.credentials.weather_token begin all = RestClient.get( "#{host}:8123/api/states#{path}" , diff --git a/app/models/event.rb b/app/models/event.rb new file mode 100644 index 0000000..fbbfb24 --- /dev/null +++ b/app/models/event.rb @@ -0,0 +1,11 @@ +class Event < ApplicationRecord + belongs_to :profile + + validates :name , presence: true + validates :text , presence: true + validates :start_date , presence: true + validates :picture , presence: true + + mount_uploader :picture, PictureUploader + +end diff --git a/app/models/member.rb b/app/models/member.rb index bf49c4b..9c892f2 100644 --- a/app/models/member.rb +++ b/app/models/member.rb @@ -7,11 +7,19 @@ class Member < ApplicationRecord mount_uploader :picture, PictureUploader has_many :entities + has_many :profiles - def admin - true + Profile.types.each do |kind| + define_method :kind do + Profile.where( member_id: self.id).where(kind: kind).first + end end + def profile( kind ) + Profile.where( member_id: self.id).where(kind: kind).first + end + def admin? - true + email == "torsten@villataika.fi" end + alias :admin :admin? end diff --git a/app/models/profile.rb b/app/models/profile.rb new file mode 100644 index 0000000..b457e83 --- /dev/null +++ b/app/models/profile.rb @@ -0,0 +1,19 @@ +class Profile < ApplicationRecord + belongs_to :member + has_many :events + + validates :name , presence: true + validates :kind , presence: true + validates :bio , presence: true + validates :picture , presence: true + + mount_uploader :picture, PictureUploader + + def self.types + ["member" , "artist" , "teacher"] + end + + def Kind + self.kind.capitalize + end +end diff --git a/app/policies/application_policy.rb b/app/policies/application_policy.rb new file mode 100644 index 0000000..0b2edd4 --- /dev/null +++ b/app/policies/application_policy.rb @@ -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 diff --git a/app/policies/edit_own_policy.rb b/app/policies/edit_own_policy.rb new file mode 100644 index 0000000..291e8ef --- /dev/null +++ b/app/policies/edit_own_policy.rb @@ -0,0 +1,14 @@ +# allows to edit/detroy own data +# which can be viewed by anyone +class EditOwnPolicy < ApplicationPolicy + def edit? + return true member.admin? + owner? + end + def owner? + member == record.member + end + alias :update? :edit? + alias :destroy? :edit? + +end diff --git a/app/policies/entity_policy.rb b/app/policies/entity_policy.rb new file mode 100644 index 0000000..652ab53 --- /dev/null +++ b/app/policies/entity_policy.rb @@ -0,0 +1,14 @@ +class EntityPolicy < ApplicationPolicy + + def owner? + member.admin? + end + alias :owner? :index? + alias :owner? :show? + alias :owner? :create? + alias :owner? :new? + alias :owner? :update? + alias :owner? :edit? + alias :owner? :destroy? + +end diff --git a/app/policies/event_policy.rb b/app/policies/event_policy.rb new file mode 100644 index 0000000..d2b1309 --- /dev/null +++ b/app/policies/event_policy.rb @@ -0,0 +1,13 @@ +class EventPolicy < EditOwnPolicy + + def owner? + member == record.profile.member + end + + class Scope < Scope + # NOTE: Be explicit about which records you allow access to! + # def resolve + # scope.all + # end + end +end diff --git a/app/policies/member_policy.rb b/app/policies/member_policy.rb new file mode 100644 index 0000000..0612a0e --- /dev/null +++ b/app/policies/member_policy.rb @@ -0,0 +1,6 @@ +class MemberPolicy < EditOwnPolicy + + + class Scope < Scope + end +end diff --git a/app/policies/profile_policy.rb b/app/policies/profile_policy.rb new file mode 100644 index 0000000..ecd1123 --- /dev/null +++ b/app/policies/profile_policy.rb @@ -0,0 +1,4 @@ +class ProfilePolicy < EditOwnPolicy + + +end diff --git a/app/views/devise/confirmations/new.html.haml b/app/views/devise/confirmations/new.html.haml index d49cb44..8ead264 100644 --- a/app/views/devise/confirmations/new.html.haml +++ b/app/views/devise/confirmations/new.html.haml @@ -15,6 +15,6 @@ 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-blue-500 hover:bg-blue-700 font-bold text-white focus:outline-none py-2 px-4 rounded focus:shadow-outline w-full" | + 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" diff --git a/app/views/devise/passwords/edit.html.haml b/app/views/devise/passwords/edit.html.haml index 96d699a..75056ee 100644 --- a/app/views/devise/passwords/edit.html.haml +++ b/app/views/devise/passwords/edit.html.haml @@ -27,6 +27,6 @@ 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-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline w-full" + 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" diff --git a/app/views/devise/passwords/new.html.haml b/app/views/devise/passwords/new.html.haml index 7ebbece..cbfe3dd 100644 --- a/app/views/devise/passwords/new.html.haml +++ b/app/views/devise/passwords/new.html.haml @@ -4,7 +4,7 @@ = 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| + 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" @@ -12,6 +12,6 @@ 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-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline w-full" | + 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" diff --git a/app/views/devise/registrations/edit.html.haml b/app/views/devise/registrations/edit.html.haml index 1c4b8fa..5181bc6 100644 --- a/app/views/devise/registrations/edit.html.haml +++ b/app/views/devise/registrations/edit.html.haml @@ -33,7 +33,7 @@ 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-blue-500 hover:bg-blue-700 font-bold text-white focus:outline-none py-2 px-4 rounded focus:shadow-outline w-full" + = 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" %p Unhappy? %span= button_to "Delete my account", registration_path(resource_name), data: { confirm: "Are you sure?" }, method: :delete diff --git a/app/views/devise/registrations/new.html.haml b/app/views/devise/registrations/new.html.haml index 36fe783..46a394c 100644 --- a/app/views/devise/registrations/new.html.haml +++ b/app/views/devise/registrations/new.html.haml @@ -5,12 +5,6 @@ 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 - = f.label :name, class: "block font-bold mb-2 text-gray-700 text-sm" - = f.text_field :name, - autocomplete: "name", - placeholder: "Pekka Juustonen", - 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, @@ -33,6 +27,6 @@ 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 "Sign Up", | - 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" | + 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" | = render "devise/shared/links" = render "devise/shared/form_footer" diff --git a/app/views/devise/sessions/new.html.haml b/app/views/devise/sessions/new.html.haml index 8f10d69..722060e 100644 --- a/app/views/devise/sessions/new.html.haml +++ b/app/views/devise/sessions/new.html.haml @@ -21,6 +21,6 @@ class: "align-baseline inline-block text-gray-700 text-sm" .mb-4 = f.submit "Log in", - class: "button bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline w-full" + 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" diff --git a/app/views/devise/unlocks/new.html.haml b/app/views/devise/unlocks/new.html.haml index 2445e8a..1f05ad9 100644 --- a/app/views/devise/unlocks/new.html.haml +++ b/app/views/devise/unlocks/new.html.haml @@ -4,7 +4,7 @@ = 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| + 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" @@ -14,6 +14,6 @@ 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" | + 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" diff --git a/app/views/entities/_form.html.haml b/app/views/entities/_form.html.haml index 627f9f4..17bac23 100644 --- a/app/views/entities/_form.html.haml +++ b/app/views/entities/_form.html.haml @@ -1,16 +1,11 @@ = simple_form_for @entity do |f| - - if @entity.errors.any? - #error_explanation - %h2= "#{pluralize(@entity.errors.count, "error")} prohibited this entity from being saved:" - %ul - - @entity.errors.full_messages.each do |message| - %li= message + = f.error_notification - .field - = f.input :name - .field - = f.input :ha_id , collection: Entity.ha_entities - .field - = f.input :member_id , collection: Member.all.collect{|m| [m.name , m.id ]} - .actions{class: button_classes} - = f.submit 'Save' + = f.input :name + = f.input :ha_id , collection: Entity.ha_entities + = f.input :member_id , collection: Member.all.collect{|m| [m.email , m.id ]} + + .flex.justify-between.mt-3 + .actions{class: button_classes} + = f.submit 'Save' + = link_to 'Back', entities_path , class: "mt-3" diff --git a/app/views/entities/edit.html.haml b/app/views/entities/edit.html.haml index 8ab6b38..cd2cb19 100644 --- a/app/views/entities/edit.html.haml +++ b/app/views/entities/edit.html.haml @@ -1,7 +1,5 @@ -%h1 Editing entity - -= render 'form' - -= link_to 'Show', @entity -\| -= link_to 'Back', entities_path +.flex.justify-center + .flex.flex-col + .text-xl.m-4 + New event + = render 'form' diff --git a/app/views/entities/index.html.haml b/app/views/entities/index.html.haml index 408fef3..5d1918d 100644 --- a/app/views/entities/index.html.haml +++ b/app/views/entities/index.html.haml @@ -1,27 +1,30 @@ -%h1 Listing entities +.flex.justify-center + %h1.text-3xl Listing entities +.flex.justify-center.mt-10 -%table - %thead - %tr - %th Name - %th Ha - %th Type - %th Member - %th - %th - %th - - %tbody - - @entities.each do |entity| + %table.table-auto.border-separate + %thead %tr - %td= entity.name - %td= entity.ha_id - %td= entity.type - %td= entity.member.name - %td= link_to 'Show', entity - %td= link_to 'Edit', edit_entity_path(entity) - %td= link_to 'Destroy', entity, method: :delete, data: { confirm: 'Are you sure?' } + %th Name + %th Ha + %th Type + %th Member + %th + %th + %th -%br + %tbody + - @entities.each do |entity| + %tr + %td.p-2= entity.name + %td.p-2= entity.ha_id + %td.p-2= entity.type + %td.p-2= entity.member.email + %td.p-2= link_to 'Show', entity + %td.p-2= link_to 'Edit', edit_entity_path(entity) + %td.p-2= link_to 'Destroy', entity, method: :delete, data: { confirm: 'Are you sure?' } -= link_to 'New Entity', new_entity_path + +.flex.justify-center.mt-10 + + = link_to 'New Entity', new_entity_path diff --git a/app/views/entities/new.html.haml b/app/views/entities/new.html.haml index dd490c0..85308ba 100644 --- a/app/views/entities/new.html.haml +++ b/app/views/entities/new.html.haml @@ -1,7 +1,5 @@ -%h1 New entity - -.grid.grid-cols-3 - %div - = render 'form' - -= link_to 'Back', entities_path +.flex.justify-center + .column + .text-xl.m-4 + New entity + = render 'form' diff --git a/app/views/entities/show.html.haml b/app/views/entities/show.html.haml index a0e4674..21cb46d 100644 --- a/app/views/entities/show.html.haml +++ b/app/views/entities/show.html.haml @@ -15,7 +15,7 @@ = @entity.value %p %b Member: - = @entity.member.name + = @entity.member.email = link_to 'Edit', edit_entity_path(@entity) \| diff --git a/app/views/events/_event.haml b/app/views/events/_event.haml new file mode 100644 index 0000000..bab0e3c --- /dev/null +++ b/app/views/events/_event.haml @@ -0,0 +1,11 @@ +%section.overflow-hidden.grid.grid-cols-1.m-5.md:m-12.lg:m-20.md:grid-cols-2 + %div + = image_for( event , "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 + = event.name + %h4.text-xl.mt-10.md:text-2xl + = event.start_date + .mt-8{ prose_classes } + = markdown(event.text) diff --git a/app/views/events/_form.html.haml b/app/views/events/_form.html.haml new file mode 100644 index 0000000..3afd5fc --- /dev/null +++ b/app/views/events/_form.html.haml @@ -0,0 +1,16 @@ += simple_form_for @event do |f| + = f.error_notification + + = f.input :name + = f.input :text + .grid.grid-cols-2.gap-10 + = f.input :start_date + = f.input :end_date + = f.input :profile_id , as: :hidden + = f.input :picture , as: :file + .flex.justify-between + %button.mt-6.bg-cyan-200.mr-3.inline-block.rounded-lg.px-4.py-3.text-md.font-medium.border.border-gray-400 + = f.submit 'Save' + = link_to member_path(current_member) do + .mt-6.ml-20.mr-3.inline-block.rounded-lg.px-4.py-3.text-md.font-medium.border.border-gray-400 + Back diff --git a/app/views/events/edit.html.haml b/app/views/events/edit.html.haml new file mode 100644 index 0000000..4a3af83 --- /dev/null +++ b/app/views/events/edit.html.haml @@ -0,0 +1,6 @@ +.flex.justify-center + .column + .text-xl.m-4 + Editing event + + = render 'form' diff --git a/app/views/events/index.html.haml b/app/views/events/index.html.haml new file mode 100644 index 0000000..65f5481 --- /dev/null +++ b/app/views/events/index.html.haml @@ -0,0 +1,12 @@ +.flex.justify-center + .grid.grid-cols-4 + - @events.each do |event| + .fex.flex-col.overflow-hidden.rounded-lg.border.border-gray-100.shadow-sm.m-10 + =link_to event do + = image_for( event , class: "h-60 w-full object-cover") + %h3.py-5.text-2xl.bg-gray-100.text-black.font-bold.text-center + = event.name + %div.h-full + .p-5.text-center + .m-2.text-sm.leading-relaxed.line-clamp-3{ prose_classes } + = shorten markdown(event.text) diff --git a/app/views/events/new.html.haml b/app/views/events/new.html.haml new file mode 100644 index 0000000..04f3297 --- /dev/null +++ b/app/views/events/new.html.haml @@ -0,0 +1,6 @@ +.flex.justify-center + .column + .text-xl.m-4 + New event + + = render 'form' diff --git a/app/views/events/show.html.haml b/app/views/events/show.html.haml new file mode 100644 index 0000000..80bbf8e --- /dev/null +++ b/app/views/events/show.html.haml @@ -0,0 +1,3 @@ += render "event" , event: @event + += render "profiles/profile" , profile: @event.profile , header: "About the teacher" diff --git a/app/views/home/torsten/code/hubfeenix.fi b/app/views/home/torsten/code/hubfeenix.fi deleted file mode 100644 index e69de29..0000000 diff --git a/app/views/layouts/_footer.haml b/app/views/layouts/_footer.haml index 15c1035..4ffed6a 100644 --- a/app/views/layouts/_footer.haml +++ b/app/views/layouts/_footer.haml @@ -14,8 +14,8 @@ %form.w-full %label.sr-only{:for => "UserEmail"} Email .p-2.border.border-gray-100.focus-within:ring.sm:flex.sm:items-center.sm:gap-4 - %input#UserEmail.w-full.border-none.focus:border-transparent.focus:ring-transparent.sm:text-sm{:placeholder => "jaakko@iki.fi", :type => "email"}/ - %button.w-full.px-6.py-3.mt-1.text-sm.font-bold.tracking-wide.text-white.uppercase.transition-none.bg-teal-500.hover:bg-teal-600.sm:mt-0.sm:w-auto.sm:flex-shrink-0 + %input.w-full.rounded.focus:border-transparent.focus:ring-transparent.sm:text-sm{:placeholder => "jaakko@iki.fi", :type => "email"}/ + %button.w-full.rounded.px-6.py-3.mt-1.text-sm.font-bold.tracking-wide.text-white.uppercase.bg-cyan-700.hover:bg-cyan-500.sm:mt-0.sm:w-auto.sm:flex-shrink-0 Sign Up .col-span-2.sm:col-span-1 %p.font-medium.text-gray-900 Main diff --git a/app/views/layouts/_header.haml b/app/views/layouts/_header.haml index 0fcce2b..1d03c5d 100644 --- a/app/views/layouts/_header.haml +++ b/app/views/layouts/_header.haml @@ -12,7 +12,7 @@ = 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"} + %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-cyan-700.hover:bg-cyan-500.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 diff --git a/app/views/layouts/_member_menu.haml b/app/views/layouts/_member_menu.haml index 24bf8de..32e4f19 100644 --- a/app/views/layouts/_member_menu.haml +++ b/app/views/layouts/_member_menu.haml @@ -3,7 +3,7 @@ %a.block.rounded-lg.px-4.py-2.text-sm.text-gray-500.hover:bg-gray-50.hover:text-gray-700{:href => "/forum", :role => "menuitem"} Forum %a.block.rounded-lg.px-4.py-2.text-sm.text-gray-500.hover:bg-gray-50.hover:text-gray-700{:href => main_app.member_path(current_member), :role => "menuitem"} - Profile + Settings - unless Rails.env.production? - if current_member.admin? %a.block.rounded-lg.px-4.py-2.text-sm.text-gray-500.hover:bg-gray-50.hover:text-gray-700{:href => merged.pages_path(), :role => "menuitem"} diff --git a/app/views/members/_form.html.haml b/app/views/members/_form.html.haml index fe415a0..25c66c8 100644 --- a/app/views/members/_form.html.haml +++ b/app/views/members/_form.html.haml @@ -1,11 +1,5 @@ -= form_for @member do |f| - .flex.flex-col - - if @member.errors.any? - #error_explanation - %h2= "#{pluralize(@member.errors.count, "error")} prohibited this member from being saved:" - %ul - - @member.errors.full_messages.each do |message| - %li= message += simple_form_for @member do |f| + = f.error_notification .grid.grid-cols-2.m-20.gap-10 .field diff --git a/app/views/members/index.html.haml b/app/views/members/index.html.haml index ea556b4..62262a8 100644 --- a/app/views/members/index.html.haml +++ b/app/views/members/index.html.haml @@ -1,25 +1,14 @@ += paginate @members .flex.justify-center - %h1 Listing members - - %table - %thead - %tr - %th Name - %th Public - %th - %th - %th - - %tbody - - @members.each do |member| - %tr - %td= member.name - %td= member.public - %td= link_to 'Show', member - %td= link_to 'Edit', edit_member_path(member) - %td= link_to 'Destroy', member, method: :delete, data: { confirm: 'Are you sure?' } - - %br - - = link_to 'New Member', new_member_path + .grid.grid-cols-4 + - @members.each do |member| + .fex.flex-col.overflow-hidden.rounded-lg.border.border-gray-100.shadow-sm.m-10 + =link_to member do + = image_for( member , class: "h-60 w-full object-cover") + %h3.pt-5.text-2xl.bg-gray-100.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 } + = shorten markdown(member.bio) diff --git a/app/views/members/show.html.haml b/app/views/members/show.html.haml index 0d1cd28..fed6c8f 100644 --- a/app/views/members/show.html.haml +++ b/app/views/members/show.html.haml @@ -1,33 +1,51 @@ -.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 - Role ? - %h5.mb-3.text-3xl.font-extrabold.leading-none.sm:text-4xl - = @member.name - .mb-8.text-gray-800 - .prose= markdown(@member.bio) - .flex.items-center - %a.inline-flex.items-center.font-semibold.transition-colors.duration-200.text-deep-purple-accent-400.hover:text-deep-purple-800{"aria-label" => "", :href => "/"} - Learn More - %svg.inline-block.w-3.ml-2{:fill => "currentColor", :viewbox => "0 0 12 12"} - %path{:d => "M9.707,5.293l-5-5A1,1,0,0,0,3.293,1.707L7.586,6,3.293,10.293a1,1,0,1,0,1.414,1.414l5-5A1,1,0,0,0,9.707,5.293Z"} +%section.mx-20 + .flex.justify-center + .px-4.py-4.md:py-10.lg:py-16.mx-5.md:mx-12.lg:mx-20.text-center + %h1.text-2xl.font-bold.tracking-tight.sm:text-4xl + Settings and profiles + %h4.text-xl.mt-4.md:text-2xl + Allowing you to create and edit various profiles + .mt-4.text-lg.pt-4 + You can have seperate profiles as teacher, artist, or coop member. + If you choose to create them they will be publicly listed. + Techers and Artists may then create events. --if @member == current_member or current_member.admin? +.grid.grid-cols-2.mx-20 + .first + - Profile.types.each do |kind| + .grid.grid-cols-4 + - if profile = @member.profile(kind) + %div + =profile.Kind + =link_to profile.name , profile_path(profile) , class: "underline" + exists + = link_to edit_profile_path(profile) do + %button.bg-cyan-200.mr-3.inline-block.rounded-lg.px-4.py-3.text-md.font-medium.border.border-gray-400 + Edit + = link_to new_event_path(profile: profile.kind) do + %button.bg-cyan-200.mr-3.inline-block.rounded-lg.px-4.py-3.text-md.font-medium.border.border-gray-400 + New #{profile.Kind} Event + -if event = profile.events.length >0 + %div + =link_to profile.events.first.name , event_path(profile.events.first) , class: "underline" + = link_to edit_event_path(profile.events.first) do + %button.bg-cyan-200.mr-3.inline-block.rounded-lg.px-4.py-3.text-md.font-medium.border.border-gray-400 + Edit + -else + No events + -else + %div Create new profile as + = link_to new_profile_path(kind: kind) do + %button.bg-cyan-200.mr-3.inline-block.rounded-lg.px-4.py-3.text-md.font-medium.border.border-gray-400 + = kind.capitalize + %div Create profile first + %div No events, create profile first - .flex.justify-around - = 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 - .grid.grid-cols-3.gap-4 - -@member.entities.each do |entity| - %div= entity.type - %div= entity.name - %div= entity.value - = 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 +.grid.grid-cols-6.gap-4.mt-10.mx-10 + -@member.entities.each do |entity| + %div= entity.type + %div= entity.name + %div= entity.value += form_tag( destroy_member_session_path , {method: :delete } ) do + %button.mt-10.ml-10.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 diff --git a/app/views/profiles/_form.html.haml b/app/views/profiles/_form.html.haml new file mode 100644 index 0000000..2926699 --- /dev/null +++ b/app/views/profiles/_form.html.haml @@ -0,0 +1,14 @@ += simple_form_for @profile do |f| + = f.error_notification + + = f.input :name + = f.input :bio + = f.input :kind, :as => :hidden + = f.input :picture , as: :file , label: (@profile.picture.blank? ? "Add picture" : "Change picture (optional)") + + .flex.justify-between.mt-4 + %button.bg-cyan-200{class: button_classes} + = f.submit 'Save' + + %button.bg-cyan-200{class: button_classes} + = link_to 'Cancel', member_path(current_member) diff --git a/app/views/profiles/_profile.haml b/app/views/profiles/_profile.haml new file mode 100644 index 0000000..32d027e --- /dev/null +++ b/app/views/profiles/_profile.haml @@ -0,0 +1,14 @@ +.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.justify-center + .px-4.py-4.text-center + %h1.text-2xl.font-bold.tracking-tight.sm:text-4xl + = header + .flex.mt-6.py-4.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 profile.picture_url + = image_tag profile.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"} + %h5.mb-3.text-3xl.font-extrabold.leading-none.sm:text-4xl + = profile.name + .mb-8.text-gray-800 + .prose= markdown(profile.bio) diff --git a/app/views/profiles/edit.html.haml b/app/views/profiles/edit.html.haml new file mode 100644 index 0000000..152f6d0 --- /dev/null +++ b/app/views/profiles/edit.html.haml @@ -0,0 +1,7 @@ +.flex.justify-center + .column + .text-xl.m-4 + Edit your + =@profile.Kind + profile + = render 'form' diff --git a/app/views/profiles/index.html.haml b/app/views/profiles/index.html.haml new file mode 100644 index 0000000..c33eb44 --- /dev/null +++ b/app/views/profiles/index.html.haml @@ -0,0 +1,14 @@ += paginate @profiles +.flex.justify-center + + .grid.grid-cols-4 + - @profiles.each do |profile| + .fex.flex-col.overflow-hidden.rounded-lg.border.border-gray-100.shadow-sm.m-10 + =link_to profile do + = image_for( profile , class: "h-60 w-full object-cover") + %h3.py-5.text-2xl.bg-gray-100.text-black.font-bold.text-center + = profile.name + %div.h-full + .p-5.text-center + .m-2.text-sm.leading-relaxed.line-clamp-3{ prose_classes } + = shorten markdown(profile.bio) diff --git a/app/views/profiles/new.html.haml b/app/views/profiles/new.html.haml new file mode 100644 index 0000000..a1e3c7f --- /dev/null +++ b/app/views/profiles/new.html.haml @@ -0,0 +1,7 @@ +.flex.justify-center + .column + .text-xl.m-4 + Create your + =@profile.kind.capitalize + profile + = render 'form' diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml new file mode 100644 index 0000000..af96b88 --- /dev/null +++ b/app/views/profiles/show.html.haml @@ -0,0 +1 @@ += render "profile" , profile: @profile , header: "#{@profile.Kind} profile" diff --git a/config/initializers/simple_form.rb b/config/initializers/simple_form.rb index 9293903..4131d36 100644 --- a/config/initializers/simple_form.rb +++ b/config/initializers/simple_form.rb @@ -14,7 +14,7 @@ SimpleForm.setup do |config| # stack. The options given below are used to wrap the # whole input. - config.wrappers :default, tag: 'div', class: '', error_class: '', valid_class: '' do |b| + config.wrappers :default, tag: 'div', class: 'mt-4', error_class: '', valid_class: '' do |b| b.use :html5 b.use :placeholder b.optional :maxlength diff --git a/config/routes.rb b/config/routes.rb index 8e96c71..fb962db 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,4 +1,6 @@ Rails.application.routes.draw do + resources :events + resources :profiles resources :entities devise_for :members diff --git a/db/migrate/20230115120517_create_profiles.rb b/db/migrate/20230115120517_create_profiles.rb new file mode 100644 index 0000000..b796cf7 --- /dev/null +++ b/db/migrate/20230115120517_create_profiles.rb @@ -0,0 +1,17 @@ +class CreateProfiles < ActiveRecord::Migration[7.0] + def change + create_table :profiles do |t| + t.string :name , null: false + t.string :kind , null: false + t.text :bio , null: false + t.string :picture , null: false + t.references :member, null: false, foreign_key: true + + t.timestamps + end + remove_column :members, :name, :string + remove_column :members, :bio, :text + remove_column :members, :picture, :string + remove_column :members, :public, :boolean + end +end diff --git a/db/migrate/20230116120518_create_events.rb b/db/migrate/20230116120518_create_events.rb new file mode 100644 index 0000000..87160f4 --- /dev/null +++ b/db/migrate/20230116120518_create_events.rb @@ -0,0 +1,14 @@ +class CreateEvents < ActiveRecord::Migration[7.0] + def change + create_table :events do |t| + t.string :name , null: false + t.text :text , null: false + t.date :start_date , null: false + t.date :end_date + t.references :profile, null: false, foreign_key: true + t.string :picture , null: false + + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index b21f953..1730331 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 2023_01_13_170732) do +ActiveRecord::Schema[7.0].define(version: 2023_01_16_120518) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -24,6 +24,18 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_13_170732) do t.index ["member_id"], name: "index_entities_on_member_id" end + create_table "events", force: :cascade do |t| + t.string "name", null: false + t.text "text", null: false + t.date "start_date", null: false + t.date "end_date" + t.bigint "profile_id", null: false + t.string "picture", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["profile_id"], name: "index_events_on_profile_id" + end + create_table "friendly_id_slugs", force: :cascade do |t| t.string "slug", null: false t.integer "sluggable_id", null: false @@ -37,10 +49,6 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_13_170732) do end create_table "members", force: :cascade do |t| - t.string "name" - t.string "bio" - t.string "picture" - t.boolean "public" t.datetime "created_at", null: false t.datetime "updated_at", null: false t.string "email", default: "", null: false @@ -56,6 +64,17 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_13_170732) do t.index ["reset_password_token"], name: "index_members_on_reset_password_token", unique: true end + create_table "profiles", force: :cascade do |t| + t.string "name", null: false + t.string "kind", null: false + t.text "bio", null: false + t.string "picture", null: false + t.bigint "member_id", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["member_id"], name: "index_profiles_on_member_id" + end + create_table "thredded_categories", force: :cascade do |t| t.bigint "messageboard_id", null: false t.text "name", null: false @@ -288,6 +307,8 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_13_170732) do end add_foreign_key "entities", "members" + add_foreign_key "events", "profiles" + add_foreign_key "profiles", "members" add_foreign_key "thredded_messageboard_users", "thredded_messageboards", on_delete: :cascade add_foreign_key "thredded_messageboard_users", "thredded_user_details", on_delete: :cascade add_foreign_key "thredded_user_post_notifications", "members", column: "user_id", on_delete: :cascade diff --git a/test/fixtures/events.yml b/test/fixtures/events.yml new file mode 100644 index 0000000..22e5b86 --- /dev/null +++ b/test/fixtures/events.yml @@ -0,0 +1,17 @@ +# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +one: + name: MyString + text: MyText + start_date: 2023-01-16 + end_date: 2023-01-16 + profile: one + picture: MyString + +two: + name: MyString + text: MyText + start_date: 2023-01-16 + end_date: 2023-01-16 + profile: two + picture: MyString diff --git a/test/fixtures/profiles.yml b/test/fixtures/profiles.yml new file mode 100644 index 0000000..7e4f2e9 --- /dev/null +++ b/test/fixtures/profiles.yml @@ -0,0 +1,13 @@ +# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +one: + name: MyString + bio: MyString + picture: MyString + member: one + +two: + name: MyString + bio: MyString + picture: MyString + member: two diff --git a/test/models/event_test.rb b/test/models/event_test.rb new file mode 100644 index 0000000..c8465c1 --- /dev/null +++ b/test/models/event_test.rb @@ -0,0 +1,7 @@ +require "test_helper" + +class EventTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end diff --git a/test/models/teacher_test.rb b/test/models/teacher_test.rb new file mode 100644 index 0000000..701636f --- /dev/null +++ b/test/models/teacher_test.rb @@ -0,0 +1,7 @@ +require "test_helper" + +class ProfileTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end diff --git a/test/policies/event_policy_test.rb b/test/policies/event_policy_test.rb new file mode 100644 index 0000000..06b0cc3 --- /dev/null +++ b/test/policies/event_policy_test.rb @@ -0,0 +1,18 @@ +require 'test_helper' + +class EventPolicyTest < ActiveSupport::TestCase + def test_scope + end + + def test_show + end + + def test_create + end + + def test_update + end + + def test_destroy + end +end diff --git a/test/policies/member_policy_test.rb b/test/policies/member_policy_test.rb new file mode 100644 index 0000000..ce08445 --- /dev/null +++ b/test/policies/member_policy_test.rb @@ -0,0 +1,18 @@ +require 'test_helper' + +class MemberPolicyTest < ActiveSupport::TestCase + def test_scope + end + + def test_show + end + + def test_create + end + + def test_update + end + + def test_destroy + end +end diff --git a/test/system/events_test.rb b/test/system/events_test.rb new file mode 100644 index 0000000..d32c4bb --- /dev/null +++ b/test/system/events_test.rb @@ -0,0 +1,51 @@ +require "application_system_test_case" + +class EventsTest < ApplicationSystemTestCase + setup do + @event = events(:one) + end + + test "visiting the index" do + visit events_url + assert_selector "h1", text: "Events" + end + + test "should create event" do + visit events_url + click_on "New event" + + fill_in "End date", with: @event.end_date + fill_in "Name", with: @event.name + fill_in "Picture", with: @event.picture + fill_in "Profile", with: @event.profile_id + fill_in "Start date", with: @event.start_date + fill_in "Text", with: @event.text + click_on "Create Event" + + assert_text "Event was successfully created" + click_on "Back" + end + + test "should update Event" do + visit event_url(@event) + click_on "Edit this event", match: :first + + fill_in "End date", with: @event.end_date + fill_in "Name", with: @event.name + fill_in "Picture", with: @event.picture + fill_in "Profile", with: @event.profile_id + fill_in "Start date", with: @event.start_date + fill_in "Text", with: @event.text + click_on "Update Event" + + assert_text "Event was successfully updated" + click_on "Back" + end + + test "should destroy Event" do + visit event_url(@event) + click_on "Destroy this event", match: :first + + assert_text "Event was successfully destroyed" + end +end diff --git a/test/system/profiles_test.rb b/test/system/profiles_test.rb new file mode 100644 index 0000000..3908394 --- /dev/null +++ b/test/system/profiles_test.rb @@ -0,0 +1,47 @@ +require "application_system_test_case" + +class ProfilesTest < ApplicationSystemTestCase + setup do + @profile = profiles(:one) + end + + test "visiting the index" do + visit profiles_url + assert_selector "h1", text: "Profiles" + end + + test "should create profile" do + visit profiles_url + click_on "New profile" + + fill_in "Bio", with: @profile.bio + fill_in "Member", with: @profile.member_id + fill_in "Name", with: @profile.name + fill_in "Picture", with: @profile.picture + click_on "Create Profile" + + assert_text "Profile was successfully created" + click_on "Back" + end + + test "should update Profile" do + visit profile_url(@profile) + click_on "Edit this profile", match: :first + + fill_in "Bio", with: @profile.bio + fill_in "Member", with: @profile.member_id + fill_in "Name", with: @profile.name + fill_in "Picture", with: @profile.picture + click_on "Update Profile" + + assert_text "Profile was successfully updated" + click_on "Back" + end + + test "should destroy Profile" do + visit profile_url(@profile) + click_on "Destroy this profile", match: :first + + assert_text "Profile was successfully destroyed" + end +end