mirror of
				https://github.com/lingble/chatwoot.git
				synced 2025-10-31 02:57:57 +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_category | ||||
|   before_action :set_article, only: [:show] | ||||
|   layout 'portal' | ||||
|  | ||||
|   def index | ||||
|     @articles = @portal.articles | ||||
| @@ -15,6 +16,7 @@ class Public::Api::V1::Portals::ArticlesController < PublicController | ||||
|  | ||||
|   def set_article | ||||
|     @article = @category.articles.find(params[:id]) | ||||
|     @parsed_content = render_article_content(@article.content) | ||||
|   end | ||||
|  | ||||
|   def set_category | ||||
| @@ -28,4 +30,10 @@ class Public::Api::V1::Portals::ArticlesController < PublicController | ||||
|   def list_params | ||||
|     params.permit(:query) | ||||
|   end | ||||
|  | ||||
|   def render_article_content(content) | ||||
|     # rubocop:disable Rails/OutputSafety | ||||
|     CommonMarker.render_html(content).html_safe | ||||
|     # rubocop:enable Rails/OutputSafety | ||||
|   end | ||||
| end | ||||
|   | ||||
| @@ -2,6 +2,7 @@ class Public::Api::V1::Portals::CategoriesController < PublicController | ||||
|   before_action :ensure_custom_domain_request, only: [:show, :index] | ||||
|   before_action :set_portal | ||||
|   before_action :set_category, only: [:show] | ||||
|   layout 'portal' | ||||
|  | ||||
|   def index | ||||
|     @categories = @portal.categories | ||||
| @@ -12,7 +13,7 @@ class Public::Api::V1::Portals::CategoriesController < PublicController | ||||
|   private | ||||
|  | ||||
|   def set_category | ||||
|     @category = @portal.categories.find_by!(locale: params[:locale]) | ||||
|     @category = @portal.categories.find_by!(locale: params[:locale], slug: params[:category_slug]) | ||||
|   end | ||||
|  | ||||
|   def set_portal | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| class Public::Api::V1::PortalsController < PublicController | ||||
|   before_action :ensure_custom_domain_request, only: [:show] | ||||
|   before_action :set_portal | ||||
|   layout 'portal' | ||||
|  | ||||
|   def show; end | ||||
|  | ||||
|   | ||||
| @@ -4,6 +4,7 @@ | ||||
|       <woot-button | ||||
|         icon="chevron-left" | ||||
|         variant="clear" | ||||
|         size="small" | ||||
|         color-scheme="primary" | ||||
|         @click="onClickGoBack" | ||||
|       > | ||||
|   | ||||
| @@ -69,7 +69,7 @@ export default { | ||||
|     }, | ||||
|     portalLink() { | ||||
|       const slug = this.$route.params.portalSlug; | ||||
|       return `/public/api/v1/portals/${slug}`; | ||||
|       return `/hc/${slug}`; | ||||
|     }, | ||||
|   }, | ||||
|   methods: { | ||||
|   | ||||
| @@ -86,7 +86,7 @@ export default { | ||||
|     }, | ||||
|     portalLink() { | ||||
|       const slug = this.$route.params.portalSlug; | ||||
|       return `/public/api/v1/portals/${slug}`; | ||||
|       return `/hc/${slug}`; | ||||
|     }, | ||||
|   }, | ||||
|   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 | ||||
|  | ||||
|   get 'hc/:slug/:locale', to: 'public/api/v1/portals#show', format: 'json' | ||||
|   get 'hc/:slug/:locale/categories', to: 'public/api/v1/portals/categories#index', format: 'json' | ||||
|   get 'hc/:slug/:locale/:category_slug', to: 'public/api/v1/portals/categories#show', format: 'json' | ||||
|   get 'hc/:slug/:locale/:category_slug/articles', to: 'public/api/v1/portals/articles#index', format: 'json' | ||||
|   get 'hc/:slug/:locale/:category_slug/:id', to: 'public/api/v1/portals/articles#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' | ||||
|   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' | ||||
|   get 'hc/:slug/:locale/:category_slug/:id', to: 'public/api/v1/portals/articles#show' | ||||
|  | ||||
|   # ---------------------------------------------------------------------- | ||||
|   # Used in mailer templates | ||||
|   | ||||
| @@ -23,6 +23,7 @@ | ||||
|     "@chatwoot/utils": "^0.0.10", | ||||
|     "@hcaptcha/vue-hcaptcha": "^0.3.2", | ||||
|     "@rails/actioncable": "6.1.3", | ||||
|     "@rails/ujs": "^7.0.3-1", | ||||
|     "@rails/webpacker": "5.3.0", | ||||
|     "@sentry/tracing": "^6.19.7", | ||||
|     "@sentry/vue": "^6.19.7", | ||||
| @@ -50,6 +51,7 @@ | ||||
|     "semver": "7.3.5", | ||||
|     "spinkit": "~1.2.5", | ||||
|     "tailwindcss": "^1.9.6", | ||||
|     "turbolinks": "^5.2.0", | ||||
|     "url-loader": "^2.0.0", | ||||
|     "v-tooltip": "~2.1.3", | ||||
|     "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" | ||||
|  | ||||
|       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 | ||||
|  | ||||
|     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, | ||||
|           params: { query: 'funny' } | ||||
|       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 | ||||
|  | ||||
| @@ -50,9 +43,6 @@ RSpec.describe 'Public Articles API', type: :request do | ||||
|       get "/hc/#{portal.slug}/#{category.locale}/#{category.slug}/#{article.id}" | ||||
|  | ||||
|       expect(response).to have_http_status(:success) | ||||
|       json_response = JSON.parse(response.body) | ||||
|  | ||||
|       expect(json_response['title']).to eql article.title | ||||
|     end | ||||
|   end | ||||
| end | ||||
|   | ||||
| @@ -14,9 +14,6 @@ RSpec.describe 'Public Portals API', type: :request do | ||||
|       get "/hc/#{portal.slug}/en" | ||||
|  | ||||
|       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 | ||||
|  | ||||
|     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" | ||||
|   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": | ||||
|   version "5.3.0" | ||||
|   resolved "https://registry.yarnpkg.com/@rails/webpacker/-/webpacker-5.3.0.tgz#9d7a615735f850572b9c5e2ad4c57f4af70d70fd" | ||||
| @@ -15087,6 +15092,11 @@ tunnel-agent@^0.6.0: | ||||
|   dependencies: | ||||
|     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: | ||||
|   version "0.14.5" | ||||
|   resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" | ||||
|   | ||||
		Reference in New Issue
	
	Block a user