mirror of
				https://github.com/lingble/chatwoot.git
				synced 2025-10-30 18:47:51 +00:00 
			
		
		
		
	feat: Sets up portal public views with rails ERB and tailwind (#5309)
* feat: Sets up portal public views with rails ERB and tailwind * linter fixes * Remove duplicate style file * Shows articles and categories * Specify layout for articles page * Updates public portal styles * Fixes blog content styles * Portal style updates for article page * Review fixes * Adds breadcrumbs * fix: rspec * fix: public portal spec * Code climate fixes * Adds test cases for missing files * Show only published articles * Updates help center routes * Review fixes * Render markdown content for aticle body * Update app/views/public/api/v1/portals/articles/index.html.erb Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com> Co-authored-by: Sojan <sojan@pepalo.com> Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com> Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com> Co-authored-by: tejaswini chile <tejaswini@chatwoot.com>
This commit is contained in:
		 Nithin David Thomas
					Nithin David Thomas
				
			
				
					committed by
					
						 GitHub
						GitHub
					
				
			
			
				
	
			
			
			 GitHub
						GitHub
					
				
			
						parent
						
							a680b08251
						
					
				
				
					commit
					1ea289e8b7
				
			| @@ -3,6 +3,7 @@ class Public::Api::V1::Portals::ArticlesController < PublicController | |||||||
|   before_action :set_portal |   before_action :set_portal | ||||||
|   before_action :set_category |   before_action :set_category | ||||||
|   before_action :set_article, only: [:show] |   before_action :set_article, only: [:show] | ||||||
|  |   layout 'portal' | ||||||
|  |  | ||||||
|   def index |   def index | ||||||
|     @articles = @portal.articles |     @articles = @portal.articles | ||||||
| @@ -15,6 +16,7 @@ class Public::Api::V1::Portals::ArticlesController < PublicController | |||||||
|  |  | ||||||
|   def set_article |   def set_article | ||||||
|     @article = @category.articles.find(params[:id]) |     @article = @category.articles.find(params[:id]) | ||||||
|  |     @parsed_content = render_article_content(@article.content) | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def set_category |   def set_category | ||||||
| @@ -28,4 +30,10 @@ class Public::Api::V1::Portals::ArticlesController < PublicController | |||||||
|   def list_params |   def list_params | ||||||
|     params.permit(:query) |     params.permit(:query) | ||||||
|   end |   end | ||||||
|  |  | ||||||
|  |   def render_article_content(content) | ||||||
|  |     # rubocop:disable Rails/OutputSafety | ||||||
|  |     CommonMarker.render_html(content).html_safe | ||||||
|  |     # rubocop:enable Rails/OutputSafety | ||||||
|  |   end | ||||||
| end | end | ||||||
|   | |||||||
| @@ -2,6 +2,7 @@ class Public::Api::V1::Portals::CategoriesController < PublicController | |||||||
|   before_action :ensure_custom_domain_request, only: [:show, :index] |   before_action :ensure_custom_domain_request, only: [:show, :index] | ||||||
|   before_action :set_portal |   before_action :set_portal | ||||||
|   before_action :set_category, only: [:show] |   before_action :set_category, only: [:show] | ||||||
|  |   layout 'portal' | ||||||
|  |  | ||||||
|   def index |   def index | ||||||
|     @categories = @portal.categories |     @categories = @portal.categories | ||||||
| @@ -12,7 +13,7 @@ class Public::Api::V1::Portals::CategoriesController < PublicController | |||||||
|   private |   private | ||||||
|  |  | ||||||
|   def set_category |   def set_category | ||||||
|     @category = @portal.categories.find_by!(locale: params[:locale]) |     @category = @portal.categories.find_by!(locale: params[:locale], slug: params[:category_slug]) | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def set_portal |   def set_portal | ||||||
|   | |||||||
| @@ -1,6 +1,7 @@ | |||||||
| class Public::Api::V1::PortalsController < PublicController | class Public::Api::V1::PortalsController < PublicController | ||||||
|   before_action :ensure_custom_domain_request, only: [:show] |   before_action :ensure_custom_domain_request, only: [:show] | ||||||
|   before_action :set_portal |   before_action :set_portal | ||||||
|  |   layout 'portal' | ||||||
|  |  | ||||||
|   def show; end |   def show; end | ||||||
|  |  | ||||||
|   | |||||||
| @@ -4,6 +4,7 @@ | |||||||
|       <woot-button |       <woot-button | ||||||
|         icon="chevron-left" |         icon="chevron-left" | ||||||
|         variant="clear" |         variant="clear" | ||||||
|  |         size="small" | ||||||
|         color-scheme="primary" |         color-scheme="primary" | ||||||
|         @click="onClickGoBack" |         @click="onClickGoBack" | ||||||
|       > |       > | ||||||
|   | |||||||
| @@ -69,7 +69,7 @@ export default { | |||||||
|     }, |     }, | ||||||
|     portalLink() { |     portalLink() { | ||||||
|       const slug = this.$route.params.portalSlug; |       const slug = this.$route.params.portalSlug; | ||||||
|       return `/public/api/v1/portals/${slug}`; |       return `/hc/${slug}`; | ||||||
|     }, |     }, | ||||||
|   }, |   }, | ||||||
|   methods: { |   methods: { | ||||||
|   | |||||||
| @@ -86,7 +86,7 @@ export default { | |||||||
|     }, |     }, | ||||||
|     portalLink() { |     portalLink() { | ||||||
|       const slug = this.$route.params.portalSlug; |       const slug = this.$route.params.portalSlug; | ||||||
|       return `/public/api/v1/portals/${slug}`; |       return `/hc/${slug}`; | ||||||
|     }, |     }, | ||||||
|   }, |   }, | ||||||
|   mounted() { |   mounted() { | ||||||
|   | |||||||
							
								
								
									
										16
									
								
								app/javascript/packs/portal.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								app/javascript/packs/portal.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | |||||||
|  | // This file is automatically compiled by Webpack, along with any other files | ||||||
|  | // present in this directory. You're encouraged to place your actual application logic in | ||||||
|  | // a relevant structure within app/javascript and only use these pack files to reference | ||||||
|  | // that code so that it will be compiled. | ||||||
|  |  | ||||||
|  | import Rails from '@rails/ujs'; | ||||||
|  | import Turbolinks from 'turbolinks'; | ||||||
|  |  | ||||||
|  | import { navigateToLocalePage } from '../portal/portalHelpers'; | ||||||
|  |  | ||||||
|  | import '../portal/application.scss'; | ||||||
|  |  | ||||||
|  | Rails.start(); | ||||||
|  | Turbolinks.start(); | ||||||
|  |  | ||||||
|  | document.addEventListener('DOMContentLoaded', navigateToLocalePage); | ||||||
							
								
								
									
										78
									
								
								app/javascript/portal/application.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								app/javascript/portal/application.scss
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,78 @@ | |||||||
|  | @import 'tailwindcss/base'; | ||||||
|  | @import 'tailwindcss/components'; | ||||||
|  | @import 'tailwindcss/utilities'; | ||||||
|  |  | ||||||
|  | @import 'widget/assets/scss/reset'; | ||||||
|  | @import 'widget/assets/scss/variables'; | ||||||
|  | @import 'widget/assets/scss/buttons'; | ||||||
|  | @import 'widget/assets/scss/mixins'; | ||||||
|  | @import 'widget/assets/scss/forms'; | ||||||
|  | @import 'shared/assets/fonts/widget_fonts'; | ||||||
|  |  | ||||||
|  | html, | ||||||
|  | body { | ||||||
|  |   font-family: $font-family; | ||||||
|  |   -moz-osx-font-smoothing: grayscale; | ||||||
|  |   -webkit-font-smoothing: antialiased; | ||||||
|  |   height: 100%; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .woot-survey-wrap { | ||||||
|  |   height: 100%; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .blog-content { | ||||||
|  |   @apply text-lg; | ||||||
|  |   @apply font-sans; | ||||||
|  |   @apply text-slate-800; | ||||||
|  |   @apply leading-normal; | ||||||
|  |  | ||||||
|  |   h1, | ||||||
|  |   h2, | ||||||
|  |   h3, | ||||||
|  |   h4, | ||||||
|  |   h5, | ||||||
|  |   h6 { | ||||||
|  |     @apply font-sans leading-relaxed font-extrabold text-slate-900; | ||||||
|  |     @apply mb-4; | ||||||
|  |     @apply mt-8; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   h1 { | ||||||
|  |     @apply text-5xl leading-normal; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   h2 { | ||||||
|  |     @apply text-4xl leading-normal; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   h3 { | ||||||
|  |     @apply text-3xl leading-normal; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   h4 { | ||||||
|  |     @apply text-2xl leading-normal; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   p { | ||||||
|  |     @apply text-lg; | ||||||
|  |     @apply font-sans; | ||||||
|  |     @apply text-slate-800; | ||||||
|  |     @apply leading-relaxed; | ||||||
|  |     @apply mb-4; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   ul { | ||||||
|  |     @apply list-disc; | ||||||
|  |     @apply pl-8; | ||||||
|  |     @apply ml-4; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   li { | ||||||
|  |     @apply text-lg; | ||||||
|  |     @apply font-sans; | ||||||
|  |     @apply text-slate-800; | ||||||
|  |     @apply leading-relaxed; | ||||||
|  |     @apply mb-2; | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										8
									
								
								app/javascript/portal/portalHelpers.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								app/javascript/portal/portalHelpers.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | |||||||
|  | export const navigateToLocalePage = () => { | ||||||
|  |   const allLocaleSwitcher = document.querySelector('.locale-switcher'); | ||||||
|  |  | ||||||
|  |   const { portalSlug } = allLocaleSwitcher.dataset; | ||||||
|  |   allLocaleSwitcher.addEventListener('change', event => { | ||||||
|  |     window.location = `/hc/${portalSlug}/${event.target.value}/`; | ||||||
|  |   }); | ||||||
|  | }; | ||||||
							
								
								
									
										23
									
								
								app/javascript/portal/specs/portal.spec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								app/javascript/portal/specs/portal.spec.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | |||||||
|  | import { navigateToLocalePage } from '../portalHelpers'; | ||||||
|  |  | ||||||
|  | describe('#navigateToLocalePage', () => { | ||||||
|  |   it('returns correct cookie name', () => { | ||||||
|  |     const elemDiv = document.createElement('div'); | ||||||
|  |     elemDiv.classList.add('locale-switcher'); | ||||||
|  |     document.body.appendChild(elemDiv); | ||||||
|  |  | ||||||
|  |     const allLocaleSwitcher = document.querySelector('.locale-switcher'); | ||||||
|  |  | ||||||
|  |     allLocaleSwitcher.addEventListener = jest | ||||||
|  |       .fn() | ||||||
|  |       .mockImplementationOnce((event, callback) => { | ||||||
|  |         callback({ target: { value: 1 } }); | ||||||
|  |       }); | ||||||
|  |  | ||||||
|  |     navigateToLocalePage(); | ||||||
|  |     expect(allLocaleSwitcher.addEventListener).toBeCalledWith( | ||||||
|  |       'change', | ||||||
|  |       expect.any(Function) | ||||||
|  |     ); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
							
								
								
									
										42
									
								
								app/views/layouts/portal.html.erb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								app/views/layouts/portal.html.erb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | |||||||
|  | <%# | ||||||
|  | # Application Layout | ||||||
|  |  | ||||||
|  | This view template is used as the layout | ||||||
|  | for every page that Administrate generates. | ||||||
|  |  | ||||||
|  | By default, it renders: | ||||||
|  | - Navigation | ||||||
|  | - Content for a search bar | ||||||
|  |   (if provided by a `content_for` block in a nested page) | ||||||
|  | - Flashes | ||||||
|  | - Links to stylesheets and JavaScripts | ||||||
|  | %> | ||||||
|  |  | ||||||
|  | <!DOCTYPE html> | ||||||
|  | <html lang="<%= I18n.locale %>"> | ||||||
|  | <head> | ||||||
|  |   <meta charset="utf-8"> | ||||||
|  |   <meta name="ROBOTS" content="NOODP"> | ||||||
|  |   <meta name="viewport" content="initial-scale=1"> | ||||||
|  |  | ||||||
|  |   <%= javascript_pack_tag 'portal' %> | ||||||
|  |   <%= stylesheet_pack_tag 'portal' %> | ||||||
|  |  | ||||||
|  |  | ||||||
|  |   <%= csrf_meta_tags %> | ||||||
|  |   <title><%= @portal.page_title%></title> | ||||||
|  | </head> | ||||||
|  | <body> | ||||||
|  |  | ||||||
|  |  | ||||||
|  |   <div class="app-container"> | ||||||
|  |     <main class="main-content min-h-screen flex flex-col" role="main"> | ||||||
|  |       <%= render "public/api/v1/portals/header", portal: @portal %> | ||||||
|  |       <%= yield %> | ||||||
|  |       <%= render "public/api/v1/portals/footer" %> | ||||||
|  |     </main> | ||||||
|  |   </div> | ||||||
|  |  | ||||||
|  |  | ||||||
|  | </body> | ||||||
|  | </html> | ||||||
							
								
								
									
										44
									
								
								app/views/public/api/v1/portals/_category-block.html.erb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								app/views/public/api/v1/portals/_category-block.html.erb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,44 @@ | |||||||
|  | <!-- This example requires Tailwind CSS v2.0+ --> | ||||||
|  | <section class="bg-white lg:container w-full py-6 px-4 flex flex-col h-full"> | ||||||
|  |   <div class="flex justify-between items-center w-full"> | ||||||
|  |     <h3 class="text-xl text-slate-900 font-semibold subpixel-antialiased leading-relaxed hover:underline""> | ||||||
|  |      <a href="/hc/<%= portal.slug %>/<%= category.locale %>/<%= category.slug %>"><%= category.name %> </a> | ||||||
|  |       </h3> <span class="text-slate-500"><%= category.articles.published.size %> articles</span> | ||||||
|  |   </div> | ||||||
|  |   <div class="py-4 w-full mt-2 flex-grow"> | ||||||
|  |     <% if category.articles.published.size == 0 %> | ||||||
|  |     <div class="h-full flex items-center justify-center bg-slate-50 rounded-xl mb-4"> | ||||||
|  |       <p class="text-sm text-slate-500">No articles here</p> | ||||||
|  |     </div> | ||||||
|  |     <% else %> | ||||||
|  |     <% category.articles.published.take(5).each do |article| %> | ||||||
|  |     <div class="flex justify-between content-center h-8 my-1"> | ||||||
|  |       <a class="text-slate-800 hover:underline leading-8" | ||||||
|  |         href="/hc/<%= portal.slug %>/<%= category.locale %>/<%= category.slug %>/<%= article.id %>" class=""><%= article.title %></a> | ||||||
|  |       <span class="flex items-center"> | ||||||
|  |         <svg class="w-4 h-4 fill-current text-slate-700" width="24" height="24" fill="none" viewBox="0 0 24 24" | ||||||
|  |           xmlns="http://www.w3.org/2000/svg"> | ||||||
|  |           <path | ||||||
|  |             d="M8.47 4.22a.75.75 0 0 0 0 1.06L15.19 12l-6.72 6.72a.75.75 0 1 0 1.06 1.06l7.25-7.25a.75.75 0 0 0 0-1.06L9.53 4.22a.75.75 0 0 0-1.06 0Z" /> | ||||||
|  |         </svg> | ||||||
|  |       </span> | ||||||
|  |     </div> | ||||||
|  |     <% end %> | ||||||
|  |     <% end %> | ||||||
|  |  | ||||||
|  |   </div> | ||||||
|  |   <div> | ||||||
|  |     <a href="/hc/<%= portal.slug %>/<%= category.locale %>/<%= category.slug %>" | ||||||
|  |       class="flex flex-row items-center text-base font-sans font-medium text-woot-600 hover:text-slate-900 hover:underline mt-4"> | ||||||
|  |  | ||||||
|  |       View all articles | ||||||
|  |       <span class="ml-2"> | ||||||
|  |         <svg class="w-4 h-4 fill-current text-woot-500" width="24" height="24" fill="none" viewBox="0 0 24 24" | ||||||
|  |           xmlns="http://www.w3.org/2000/svg"> | ||||||
|  |           <path | ||||||
|  |             d="M13.267 4.209a.75.75 0 0 0-1.034 1.086l6.251 5.955H3.75a.75.75 0 0 0 0 1.5h14.734l-6.251 5.954a.75.75 0 0 0 1.034 1.087l7.42-7.067a.996.996 0 0 0 .3-.58.758.758 0 0 0-.001-.29.995.995 0 0 0-.3-.578l-7.419-7.067Z" /> | ||||||
|  |         </svg> | ||||||
|  |       </span> | ||||||
|  |     </a> | ||||||
|  |   </div> | ||||||
|  | </section> | ||||||
							
								
								
									
										6
									
								
								app/views/public/api/v1/portals/_footer.html.erb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								app/views/public/api/v1/portals/_footer.html.erb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | |||||||
|  | <!-- This example requires Tailwind CSS v2.0+ --> | ||||||
|  | <footer class="bg-slate-50 py-16 flex flex-col items-center justify-center"> | ||||||
|  |   <div class="mx-auto max-w-2xl"> | ||||||
|  |     <p class="text-slate-700 py-2 text-center">Made with <a href="/" target="_blank">Chatwoot 💙</a>.</p> | ||||||
|  |   </div> | ||||||
|  | </footer> | ||||||
							
								
								
									
										37
									
								
								app/views/public/api/v1/portals/_header.html.erb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								app/views/public/api/v1/portals/_header.html.erb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | |||||||
|  | <!-- This example requires Tailwind CSS v2.0+ --> | ||||||
|  | <header class="bg-white mx-auto px-4 max-w-4xl w-full border border-slate-600"> | ||||||
|  |   <nav class="px-0 flex" aria-label="Top"> | ||||||
|  |     <div class="w-full py-4 flex items-center justify-between"> | ||||||
|  |       <div class="flex items-center"> | ||||||
|  |         <a href="#"> | ||||||
|  |           <span class="sr-only"><%= portal.name %>%></span> | ||||||
|  |           <img class="h-8 w-auto" | ||||||
|  |             src="https://d33wubrfki0l68.cloudfront.net/973467c532160fd8b940300a43fa85fa2d060307/dc9a0/static/brand-73f58cdefae282ae74cebfa74c1d7003.svg" | ||||||
|  |             alt=""> | ||||||
|  |         </a> | ||||||
|  |         <div class="ml-8 border-l-1 border-slate-50"> | ||||||
|  |         <div class="flex-grow flex-shrink-0"> | ||||||
|  |         <a href="#" class="flex flex-row items-center text-sm font-sans font-medium text-slate-700 hover:text-slate-800 hover:underline"> Goto main site  | ||||||
|  |           <span class="ml-2">  | ||||||
|  |             <svg class="w-4 h-4 fill-current text-slate-600" width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M13.267 4.209a.75.75 0 0 0-1.034 1.086l6.251 5.955H3.75a.75.75 0 0 0 0 1.5h14.734l-6.251 5.954a.75.75 0 0 0 1.034 1.087l7.42-7.067a.996.996 0 0 0 .3-.58.758.758 0 0 0-.001-.29.995.995 0 0 0-.3-.578l-7.419-7.067Z" /></svg> | ||||||
|  |           </span> | ||||||
|  |         </a> | ||||||
|  |       </div> | ||||||
|  |         </div> | ||||||
|  |       </div> | ||||||
|  |     </div> | ||||||
|  |     <div class="flex flex-grow justify-end flex-shrink-0 items-center"> | ||||||
|  |        | ||||||
|  |       <div> | ||||||
|  |       <select  | ||||||
|  |       value="<%= @portal.config["default_locale"] %>" | ||||||
|  |       data-portal-slug="<%= @portal.slug %>" | ||||||
|  |       class="h-8 block w-full flex-shrink bg-slate-50 border border-slate-200 text-slate-700 py-1 px-4 pr-8 rounded leading-tight text-base font-medium focus:outline-none focus:bg-white focus:border-slate-500 locale-switcher"> | ||||||
|  |         <% @portal.config["allowed_locales"].each do |locale| %> | ||||||
|  |          <option value="<%= locale %>"><%= locale %></option> | ||||||
|  |         <% end %> | ||||||
|  |       </select> | ||||||
|  |       </div> | ||||||
|  |     </div> | ||||||
|  |   </nav> | ||||||
|  | </header> | ||||||
							
								
								
									
										7
									
								
								app/views/public/api/v1/portals/_hero.html.erb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								app/views/public/api/v1/portals/_hero.html.erb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | |||||||
|  | <!-- This example requires Tailwind CSS v2.0+ --> | ||||||
|  | <section class="bg-slate-50 py-24 flex flex-col items-center justify-center"> | ||||||
|  |   <div class="mx-auto max-w-2xl"> | ||||||
|  |     <h1 class="text-4xl text-slate-900 font-semibold subpixel-antialiased leading-relaxed text-center"><%= portal.header_text %></h1> | ||||||
|  |     <p class="text-slate-700 py-2 text-center">Search for the articles here or browse the categories below.</p> | ||||||
|  |   </div> | ||||||
|  | </section> | ||||||
							
								
								
									
										35
									
								
								app/views/public/api/v1/portals/articles/index.html.erb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								app/views/public/api/v1/portals/articles/index.html.erb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | |||||||
|  |  | ||||||
|  | <div class="bg-slate-50"> | ||||||
|  | 	<div class="max-w-4xl px-6 py-16 mx-auto space-y-12 w-full"> | ||||||
|  | 		<div class="space-y-4"> | ||||||
|  | 		<div> | ||||||
|  | 				<a class="text-slate-800 hover:underline leading-8" | ||||||
|  |         href="/hc/<%= @portal.slug %>/<%= @category.slug %>" class=""><%= @portal.name %> Home</a> | ||||||
|  | 		<span>/</span> | ||||||
|  | 		<span>/</span> | ||||||
|  | 		</div> | ||||||
|  | 			<% @articles.each do |article| %> | ||||||
|  | 				<h1 class="text-4xl font-bold md:tracking-normal leading-snug md:text-5xl text-slate-900"> | ||||||
|  | 				<%= article.title %></h1> | ||||||
|  | 				<div class="flex flex-col items-start justify-between w-full md:flex-row md:items-center pt-2"> | ||||||
|  | 					<div class="flex items-center space-x-2"> | ||||||
|  | 						<img src="<%= article.author.avatar_url %>" alt="" class="w-12 h-812 border rounded-full"> | ||||||
|  | 						<div> | ||||||
|  | 							<h5 class="text-base font-medium text-slate-900 mb-2"><%= article.author.name %></h5> | ||||||
|  | 							<p class="text-sm font-normal text-slate-700"> | ||||||
|  | 								<%= article.author.updated_at.strftime("%B %d %Y") %></p> | ||||||
|  | 						</div> | ||||||
|  | 					</div> | ||||||
|  | 				</div> | ||||||
|  | 			<% end %> | ||||||
|  | 		</div> | ||||||
|  | 	</div> | ||||||
|  |  | ||||||
|  | </div> | ||||||
|  | <div class="max-w-4xl flex-grow w-full px-6 py-16 mx-auto space-y-12"> | ||||||
|  | 	<article class="space-y-8 "> | ||||||
|  | 		<div class="text-slate-800 font-sans leading-8 text-lg subpixel-antialiased max-w-3xl blog-content"> | ||||||
|  | 		</div> | ||||||
|  | 	</article> | ||||||
|  | </div> | ||||||
|  |  | ||||||
							
								
								
									
										35
									
								
								app/views/public/api/v1/portals/articles/show.html.erb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								app/views/public/api/v1/portals/articles/show.html.erb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | |||||||
|  | <div class="bg-slate-50"> | ||||||
|  | 	<div class="max-w-4xl px-6 py-16 mx-auto space-y-4 w-full"> | ||||||
|  |  | ||||||
|  | 			<div> | ||||||
|  | 				<a class="text-slate-700 hover:underline leading-8 text-sm font-medium" | ||||||
|  | 					href="/hc/<%= @portal.slug %>/<%= @article.category.locale %>" class=""><%= @portal.name %> Home</a> | ||||||
|  | 				<span class="text-xs text-slate-600 px-1">/</span> | ||||||
|  | 				<a class="text-slate-700 hover:underline leading-8 text-sm font-medium" | ||||||
|  | 					href="/hc/<%= @portal.slug %>/<%= @article.category.locale %>/<%= @article.category.slug %>" | ||||||
|  | 					class=""><%= @article.category.name %></a> | ||||||
|  | 				<span class="text-xs text-slate-600 px-1">/</span> | ||||||
|  | 			</div> | ||||||
|  | 			<h1 class="text-4xl font-bold md:tracking-normal leading-snug md:text-5xl text-slate-900"> | ||||||
|  | 				<%= @article.title %></h1> | ||||||
|  | 			<div class="flex flex-col items-start justify-between w-full md:flex-row md:items-center pt-2"> | ||||||
|  | 				<div class="flex items-center md:space-x-2"> | ||||||
|  | 					<img src="<%= @article.author.avatar_url %>" alt="" class="w-12 h-812 border rounded-full"> | ||||||
|  | 					<div> | ||||||
|  | 						<h5 class="text-base font-medium text-slate-900 mb-2"><%= @article.author.name %></h5> | ||||||
|  | 						<p class="text-sm font-normal text-slate-700"> | ||||||
|  | 							<%= @article.author.updated_at.strftime("%B %d %Y") %></p> | ||||||
|  | 					</div> | ||||||
|  | 				</div> | ||||||
|  | 			</div> | ||||||
|  |  | ||||||
|  | 	</div> | ||||||
|  |  | ||||||
|  | </div> | ||||||
|  | <div class="max-w-4xl flex-grow w-full px-6 py-16 mx-auto space-y-12"> | ||||||
|  | 	<article class="space-y-8 "> | ||||||
|  | 		<div class="text-slate-800 font-sans leading-8 text-lg subpixel-antialiased max-w-3xl blog-content"> | ||||||
|  | 			<p><%= @parsed_content %></p> | ||||||
|  | 		</div> | ||||||
|  | 	</article> | ||||||
|  | </div> | ||||||
| @@ -0,0 +1,44 @@ | |||||||
|  | <!-- This example requires Tailwind CSS v2.0+ --> | ||||||
|  | <section class="bg-white lg:container w-full py-6 px-4 flex flex-col h-full"> | ||||||
|  |   <div class="flex justify-between items-center w-full"> | ||||||
|  |     <h3 class="text-xl text-slate-900 font-semibold subpixel-antialiased leading-relaxed hover:underline""> | ||||||
|  |      <a href="/hc/<%= portal.slug %>/<%= category.locale %>/<%= category.slug %>"><%= category.name %> </a> | ||||||
|  |       </h3> <span class="text-slate-500"><%= category.articles.published.size %> articles</span> | ||||||
|  |   </div> | ||||||
|  |   <div class="py-4 w-full mt-2 flex-grow"> | ||||||
|  |     <% if category.articles.published.size == 0 %> | ||||||
|  |     <div class="h-full flex items-center justify-center bg-slate-50 rounded-xl mb-4"> | ||||||
|  |       <p class="text-sm text-slate-500">No articles here</p> | ||||||
|  |     </div> | ||||||
|  |     <% else %> | ||||||
|  |     <% category.articles.published.take(5).each do |article| %> | ||||||
|  |     <div class="flex justify-between content-center h-8 my-1"> | ||||||
|  |       <a class="text-slate-800 hover:underline leading-8" | ||||||
|  |         href="/hc/<%= portal.slug %>/<%= category.locale %>/<%= category.slug %>/<%= article.id %>" class=""><%= article.title %></a> | ||||||
|  |       <span class="flex items-center"> | ||||||
|  |         <svg class="w-4 h-4 fill-current text-slate-700" width="24" height="24" fill="none" viewBox="0 0 24 24" | ||||||
|  |           xmlns="http://www.w3.org/2000/svg"> | ||||||
|  |           <path | ||||||
|  |             d="M8.47 4.22a.75.75 0 0 0 0 1.06L15.19 12l-6.72 6.72a.75.75 0 1 0 1.06 1.06l7.25-7.25a.75.75 0 0 0 0-1.06L9.53 4.22a.75.75 0 0 0-1.06 0Z" /> | ||||||
|  |         </svg> | ||||||
|  |       </span> | ||||||
|  |     </div> | ||||||
|  |     <% end %> | ||||||
|  |     <% end %> | ||||||
|  |  | ||||||
|  |   </div> | ||||||
|  |   <div> | ||||||
|  |     <a href="/hc/<%= portal.slug %>/<%= category.locale %>/<%= category.slug %>" | ||||||
|  |       class="flex flex-row items-center text-base font-sans font-medium text-woot-600 hover:text-slate-900 hover:underline mt-4"> | ||||||
|  |  | ||||||
|  |       View all articles | ||||||
|  |       <span class="ml-2"> | ||||||
|  |         <svg class="w-4 h-4 fill-current text-woot-500" width="24" height="24" fill="none" viewBox="0 0 24 24" | ||||||
|  |           xmlns="http://www.w3.org/2000/svg"> | ||||||
|  |           <path | ||||||
|  |             d="M13.267 4.209a.75.75 0 0 0-1.034 1.086l6.251 5.955H3.75a.75.75 0 0 0 0 1.5h14.734l-6.251 5.954a.75.75 0 0 0 1.034 1.087l7.42-7.067a.996.996 0 0 0 .3-.58.758.758 0 0 0-.001-.29.995.995 0 0 0-.3-.578l-7.419-7.067Z" /> | ||||||
|  |         </svg> | ||||||
|  |       </span> | ||||||
|  |     </a> | ||||||
|  |   </div> | ||||||
|  | </section> | ||||||
| @@ -0,0 +1,7 @@ | |||||||
|  | <!-- This example requires Tailwind CSS v2.0+ --> | ||||||
|  | <section class="bg-slate-50 py-24 flex flex-col items-center justify-center"> | ||||||
|  |   <div class="mx-auto max-w-2xl"> | ||||||
|  |     <h1 class="text-4xl text-slate-900 font-semibold subpixel-antialiased leading-relaxed text-center"><%= portal.header_text %></h1> | ||||||
|  |     <p class="text-slate-700 py-2 text-center">Search for the articles here or browse the categories below.</p> | ||||||
|  |   </div> | ||||||
|  | </section> | ||||||
							
								
								
									
										11
									
								
								app/views/public/api/v1/portals/categories/index.html.erb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								app/views/public/api/v1/portals/categories/index.html.erb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | |||||||
|  | <%= render "hero", portal: @portal %> | ||||||
|  |  | ||||||
|  | <div class="max-w-4xl w-full flex-grow mx-auto py-16"> | ||||||
|  |   <div class="grid grid-cols-2 gap-x-32 gap-y-12"> | ||||||
|  |     <% @categories.each do |category| %> | ||||||
|  |       <%= render "category-block", category: category, portal: @portal %> | ||||||
|  |     <% end %> | ||||||
|  |   </div> | ||||||
|  | </div> | ||||||
|  |  | ||||||
|  |  | ||||||
							
								
								
									
										41
									
								
								app/views/public/api/v1/portals/categories/show.html.erb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								app/views/public/api/v1/portals/categories/show.html.erb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | |||||||
|  |  | ||||||
|  | <div class="bg-slate-50"> | ||||||
|  |     <div class="max-w-4xl px-6 py-16 mx-auto space-y-8"> | ||||||
|  |         <a class="text-slate-700 text-sm hover:underline leading-8" | ||||||
|  |         href="/hc/<%= @portal.slug %>/<%= @category.locale %>" class=""><%= @portal.name %> Home</a> | ||||||
|  | 		<span class="text-xs text-slate-600 px-1">/</span> | ||||||
|  |          | ||||||
|  |         <div class="flex justify-start items-center w-full"> | ||||||
|  |             <h1 class="text-3xl font-bold md:tracking-normal leading-snug text-slate-900"> | ||||||
|  |                 <%= @category.name %></h1> | ||||||
|  |             <span class="text-slate-500 px-8"><%= @category.articles.published.size %> articles</span> | ||||||
|  |         </div> | ||||||
|  |     </div> | ||||||
|  |  | ||||||
|  | </div> | ||||||
|  | <section class="bg-white max-w-4xl w-full mx-auto py-6 px-4 flex flex-col items-center justify-center flex-grow"> | ||||||
|  |  | ||||||
|  |     <div class="py-4 w-full mt-2 flex-grow"> | ||||||
|  |         <% if @category.articles.published.size == 0 %> | ||||||
|  |         <div class="h-full flex items-center justify-center bg-slate-50 rounded-xl"> | ||||||
|  |             <p class="text-sm text-slate-500">No articles here</p> | ||||||
|  |         </div> | ||||||
|  |         <% else %> | ||||||
|  |         <% @category.articles.published.each do |article| %> | ||||||
|  |         <div class="flex justify-between content-center h-8 my-1"> | ||||||
|  |             <a class="text-slate-800 hover:underline" | ||||||
|  |                 href="/hc/<%= @portal.slug %>/<%= @category.locale %>/<%= @category.slug %>/<%= article.id %>" | ||||||
|  |                 class=""><%= article.title %></a> | ||||||
|  |             <span> | ||||||
|  |                 <svg class="w-4 h-4 fill-current text-slate-700" width="24" height="24" fill="none" viewBox="0 0 24 24" | ||||||
|  |                     xmlns="http://www.w3.org/2000/svg"> | ||||||
|  |                     <path | ||||||
|  |                         d="M8.47 4.22a.75.75 0 0 0 0 1.06L15.19 12l-6.72 6.72a.75.75 0 1 0 1.06 1.06l7.25-7.25a.75.75 0 0 0 0-1.06L9.53 4.22a.75.75 0 0 0-1.06 0Z" /> | ||||||
|  |                 </svg> | ||||||
|  |             </span> | ||||||
|  |         </div> | ||||||
|  |         <% end %> | ||||||
|  |         <% end %> | ||||||
|  |     </div> | ||||||
|  | </section> | ||||||
|  |  | ||||||
							
								
								
									
										12
									
								
								app/views/public/api/v1/portals/show.html.erb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								app/views/public/api/v1/portals/show.html.erb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | |||||||
|  |  | ||||||
|  | <%= render "hero", portal: @portal %> | ||||||
|  |  | ||||||
|  | <div class="max-w-4xl w-full flex-grow mx-auto py-16"> | ||||||
|  |   <div class="grid grid-cols-2 gap-x-32 gap-y-12"> | ||||||
|  |     <% @portal.categories.each do |category| %> | ||||||
|  |       <%= render "category-block", category: category, portal: @portal %> | ||||||
|  |     <% end %> | ||||||
|  |   </div> | ||||||
|  | </div> | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -287,11 +287,11 @@ Rails.application.routes.draw do | |||||||
|     end |     end | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   get 'hc/:slug/:locale', to: 'public/api/v1/portals#show', format: 'json' |   get 'hc/:slug/:locale', to: 'public/api/v1/portals#show' | ||||||
|   get 'hc/:slug/:locale/categories', to: 'public/api/v1/portals/categories#index', format: 'json' |   get 'hc/:slug/:locale/categories', to: 'public/api/v1/portals/categories#index' | ||||||
|   get 'hc/:slug/:locale/:category_slug', to: 'public/api/v1/portals/categories#show', format: 'json' |   get 'hc/:slug/:locale/:category_slug', to: 'public/api/v1/portals/categories#show' | ||||||
|   get 'hc/:slug/:locale/:category_slug/articles', to: 'public/api/v1/portals/articles#index', format: 'json' |   get 'hc/:slug/:locale/:category_slug/articles', to: 'public/api/v1/portals/articles#index' | ||||||
|   get 'hc/:slug/:locale/:category_slug/:id', to: 'public/api/v1/portals/articles#show', format: 'json' |   get 'hc/:slug/:locale/:category_slug/:id', to: 'public/api/v1/portals/articles#show' | ||||||
|  |  | ||||||
|   # ---------------------------------------------------------------------- |   # ---------------------------------------------------------------------- | ||||||
|   # Used in mailer templates |   # Used in mailer templates | ||||||
|   | |||||||
| @@ -23,6 +23,7 @@ | |||||||
|     "@chatwoot/utils": "^0.0.10", |     "@chatwoot/utils": "^0.0.10", | ||||||
|     "@hcaptcha/vue-hcaptcha": "^0.3.2", |     "@hcaptcha/vue-hcaptcha": "^0.3.2", | ||||||
|     "@rails/actioncable": "6.1.3", |     "@rails/actioncable": "6.1.3", | ||||||
|  |     "@rails/ujs": "^7.0.3-1", | ||||||
|     "@rails/webpacker": "5.3.0", |     "@rails/webpacker": "5.3.0", | ||||||
|     "@sentry/tracing": "^6.19.7", |     "@sentry/tracing": "^6.19.7", | ||||||
|     "@sentry/vue": "^6.19.7", |     "@sentry/vue": "^6.19.7", | ||||||
| @@ -50,6 +51,7 @@ | |||||||
|     "semver": "7.3.5", |     "semver": "7.3.5", | ||||||
|     "spinkit": "~1.2.5", |     "spinkit": "~1.2.5", | ||||||
|     "tailwindcss": "^1.9.6", |     "tailwindcss": "^1.9.6", | ||||||
|  |     "turbolinks": "^5.2.0", | ||||||
|     "url-loader": "^2.0.0", |     "url-loader": "^2.0.0", | ||||||
|     "v-tooltip": "~2.1.3", |     "v-tooltip": "~2.1.3", | ||||||
|     "videojs-record": "^4.5.0", |     "videojs-record": "^4.5.0", | ||||||
|   | |||||||
| @@ -20,10 +20,6 @@ RSpec.describe 'Public Articles API', type: :request do | |||||||
|       get "/hc/#{portal.slug}/#{category.locale}/#{category.slug}/articles" |       get "/hc/#{portal.slug}/#{category.locale}/#{category.slug}/articles" | ||||||
|  |  | ||||||
|       expect(response).to have_http_status(:success) |       expect(response).to have_http_status(:success) | ||||||
|       json_response = JSON.parse(response.body) |  | ||||||
|  |  | ||||||
|       expect(json_response['payload'].length).to eql portal.articles.count |  | ||||||
|       expect(json_response['meta']['articles_count']).to be json_response['payload'].size |  | ||||||
|     end |     end | ||||||
|  |  | ||||||
|     it 'get all articles with searched text query' do |     it 'get all articles with searched text query' do | ||||||
| @@ -39,9 +35,6 @@ RSpec.describe 'Public Articles API', type: :request do | |||||||
|           headers: agent.create_new_auth_token, |           headers: agent.create_new_auth_token, | ||||||
|           params: { query: 'funny' } |           params: { query: 'funny' } | ||||||
|       expect(response).to have_http_status(:success) |       expect(response).to have_http_status(:success) | ||||||
|       json_response = JSON.parse(response.body) |  | ||||||
|       expect(json_response['payload'].count).to be 1 |  | ||||||
|       expect(json_response['meta']['articles_count']).to be json_response['payload'].size |  | ||||||
|     end |     end | ||||||
|   end |   end | ||||||
|  |  | ||||||
| @@ -50,9 +43,6 @@ RSpec.describe 'Public Articles API', type: :request do | |||||||
|       get "/hc/#{portal.slug}/#{category.locale}/#{category.slug}/#{article.id}" |       get "/hc/#{portal.slug}/#{category.locale}/#{category.slug}/#{article.id}" | ||||||
|  |  | ||||||
|       expect(response).to have_http_status(:success) |       expect(response).to have_http_status(:success) | ||||||
|       json_response = JSON.parse(response.body) |  | ||||||
|  |  | ||||||
|       expect(json_response['title']).to eql article.title |  | ||||||
|     end |     end | ||||||
|   end |   end | ||||||
| end | end | ||||||
|   | |||||||
| @@ -14,9 +14,6 @@ RSpec.describe 'Public Portals API', type: :request do | |||||||
|       get "/hc/#{portal.slug}/en" |       get "/hc/#{portal.slug}/en" | ||||||
|  |  | ||||||
|       expect(response).to have_http_status(:success) |       expect(response).to have_http_status(:success) | ||||||
|       json_response = JSON.parse(response.body) |  | ||||||
|       expect(json_response['slug']).to eql 'test-portal' |  | ||||||
|       expect(json_response['meta']['articles_count']).to be 0 |  | ||||||
|     end |     end | ||||||
|  |  | ||||||
|     it 'Throws unauthorised error for unknown domain' do |     it 'Throws unauthorised error for unknown domain' do | ||||||
|   | |||||||
							
								
								
									
										10
									
								
								yarn.lock
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								yarn.lock
									
									
									
									
									
								
							| @@ -1878,6 +1878,11 @@ | |||||||
|   resolved "https://registry.yarnpkg.com/@rails/actioncable/-/actioncable-6.1.3.tgz#c8a67ec4d22ecd6931f7ebd98143fddbc815419a" |   resolved "https://registry.yarnpkg.com/@rails/actioncable/-/actioncable-6.1.3.tgz#c8a67ec4d22ecd6931f7ebd98143fddbc815419a" | ||||||
|   integrity sha512-m02524MR9cTnUNfGz39Lkx9jVvuL0tle4O7YgvouJ7H83FILxzG1nQ5jw8pAjLAr9XQGu+P1sY4SKE3zyhCNjw== |   integrity sha512-m02524MR9cTnUNfGz39Lkx9jVvuL0tle4O7YgvouJ7H83FILxzG1nQ5jw8pAjLAr9XQGu+P1sY4SKE3zyhCNjw== | ||||||
|  |  | ||||||
|  | "@rails/ujs@^7.0.3-1": | ||||||
|  |   version "7.0.3-1" | ||||||
|  |   resolved "https://registry.yarnpkg.com/@rails/ujs/-/ujs-7.0.3-1.tgz#0a0f4f2b22b887bcbf6e0b0a72b8c86665cd31d9" | ||||||
|  |   integrity sha512-g3LgpBAsWmW97xFxh5OTDgyEJLt63fEENJUYb/iNFRXY6aKLI/by6MjFw7x492DSP/+vKQa3oMEdNnjI9+yZgQ== | ||||||
|  |  | ||||||
| "@rails/webpacker@5.3.0": | "@rails/webpacker@5.3.0": | ||||||
|   version "5.3.0" |   version "5.3.0" | ||||||
|   resolved "https://registry.yarnpkg.com/@rails/webpacker/-/webpacker-5.3.0.tgz#9d7a615735f850572b9c5e2ad4c57f4af70d70fd" |   resolved "https://registry.yarnpkg.com/@rails/webpacker/-/webpacker-5.3.0.tgz#9d7a615735f850572b9c5e2ad4c57f4af70d70fd" | ||||||
| @@ -15087,6 +15092,11 @@ tunnel-agent@^0.6.0: | |||||||
|   dependencies: |   dependencies: | ||||||
|     safe-buffer "^5.0.1" |     safe-buffer "^5.0.1" | ||||||
|  |  | ||||||
|  | turbolinks@^5.2.0: | ||||||
|  |   version "5.2.0" | ||||||
|  |   resolved "https://registry.yarnpkg.com/turbolinks/-/turbolinks-5.2.0.tgz#e6877a55ea5c1cb3bb225f0a4ae303d6d32ff77c" | ||||||
|  |   integrity sha512-pMiez3tyBo6uRHFNNZoYMmrES/IaGgMhQQM+VFF36keryjb5ms0XkVpmKHkfW/4Vy96qiGW3K9bz0tF5sK9bBw== | ||||||
|  |  | ||||||
| tweetnacl@^0.14.3, tweetnacl@~0.14.0: | tweetnacl@^0.14.3, tweetnacl@~0.14.0: | ||||||
|   version "0.14.5" |   version "0.14.5" | ||||||
|   resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" |   resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user