SarwarAhmed wrote a comment+100 XP
1mo ago
@Alext1926 What is your GitHub ID?
SarwarAhmed liked a comment+100 XP
1mo ago
** Warning **
Do not just use ->markdown() on its own this is prome to XSS (Cross-Site-Scripting) if you was to put <img src="#" onmouseover="alert('hacked');" /> in your idea description or worse a user was, when they hover over the image, an alert will show. Instead use:
`return Attribute::get(
fn ($value, $attributes) => new HtmlString(str($attributes['description'])->markdown([
'html_input' => 'escape',
'allow_unsafe_links' => false,
'max_nesting_level' => 5,
])));`
SarwarAhmed liked a comment+100 XP
1mo ago
Thanks @marc_meat! 🙌🏻 After investigating, I confirmed that this is indeed the error. I found a possible workaround without modifying the Composer fork: dynamically setting the enctype with Alpine.js only when the user selects an image:
x-bind:enctype="hasImage ? 'multipart/form-data' : false"
This way, the form is submitted without multipart/form-data by default, and the browser test passes, but when the user selects an image, it changes automatically. I hope this helps!
SarwarAhmed liked a comment+100 XP
1mo ago
SarwarAhmed wrote a comment+100 XP
1mo ago
<x-layout>
<div>
<header class="py-8 md:py-12">
<h1 class="text-3xl font-bold">Ideas</h1>
<p class="text-muted-foreground text-sm mt-2">Capture your thoughts. Make a plan</p>
<x-card
x-data
@click="$dispatch('open-modal', 'create-idea')"
is="button"
type="button"
data-testid="create-idea-button"
class="mt-10 cursor-pointer h-32 w-full text-left"
>
<p>What's the idea</p>
</x-card>
</header>
<div>
{{-- all status--}}
<a href="/ideas" class="btn {{ request()->has('status') ? 'btn-outlined' : '' }}">ALL</a>
{{-- status filter --}}
@foreach(\App\IdeaStatus::cases() as $status)
<a
href="/ideas?status={{ $status->value }}"
class="btn {{ request('status') === $status->value ? '' : 'btn-outlined' }}"
>
{{ $status->label() }} <span>{{ $statusCounts->get($status->value) }}</span>
</a>
@endforeach
</div>
<div class="mt-10 text-muted-foreground">
<div class="grid md:grid-cols-2 gap-6">
@forelse($ideas as $idea)
<x-card href="{{ route('idea.show', $idea) }}">
@if($idea->image_path)
<div class="mb-4 -mx-4 -mt-4 rounded-t-lg overflow-hidden">
<img src="{{ asset('storage/' . $idea->image_path)}}" alt=""
class="w-full h-auto object-cover">
</div>
@endif
<h3 class="text-foreground text-lg">{{$idea->title}}</h3>
<div class="mt-1">
<x-ideas.status-label status="{{ $idea->status }}">
{{ $idea->status->label() }}
</x-ideas.status-label>
</div>
<div class="mt-5 line-clamp-2">{{ $idea->description }}</div>
<div class="mt-4">{{ $idea->created_at->diffForHumans() }}</div>
</x-card>
@empty
<x-card>
<p>No ideas at this time.</p>
</x-card>
@endforelse
</div>
</div>
<x-modal name="create-idea" title="New Idea">
<form
x-data="{
status: 'pending',
newLink: '',
links: [],
newStep: '',
steps: []
}"
method="POST"
action="{{ route('idea.store') }}"
enctype="multipart/form-data"
class="space-y-6"
>
@csrf
<div class="space-y-6">
<x-form.field
label="Title"
name="title"
placeholder="What's your idea?"
required
autofocus
/>
<div class="space-y-2">
<label for="status" class="label">Status</label>
<div class="flex gap-x-3">
@foreach(\App\IdeaStatus::cases() as $status)
<button
type="button"
@click="status = @js($status->value)"
data-test="button-status-{{ $status->value }}"
class="btn flex-1 h-10"
:class="{'btn-outlined': status !== @js($status->value)}"
>
{{ $status->label() }}
</button>
@endforeach
<input type="hidden" name="status" :value="status">
</div>
<x-form.error name="status"/>
</div>
<x-form.field
label="Description"
name="description"
type="textarea"
placeholder="Describe your idea in detail"
/>
<div class="space-y-2">
<label for="image" class="label">Featured image</label>
<input type="file" name="image" accept="image/*">
<x-form.error class="error" name="image"/>
</div>
<div>
<fieldset class="space-y-3">
<legend class="label">Actionable Steps</legend>
<template x-for="(step, index) in steps" :key="index">
<div class="flex gap-x-2 items-center">
<input type="text" name="steps[]" x-model="step" class="input" readonly>
<button
aria-label="Remove step"
@click="steps.splice(index, 1)"
>
<x-zondicon-close class="h-5 w-5 form-muted-icon"/>
</button>
</div>
</template>
<div class="flex gap-x-2 items-center">
<input
x-model="newStep"
id="new-step"
data-test="new-step"
placeholder="What needs to be done?"
class="input flex-1"
spellcheck="false"
>
<button
type="button"
@click="steps.push(newStep.trim()); newStep = ''"
data-test="submit-new-step-button"
:disabled="!newStep.trim().length"
aria-label="Add a new step"
>
<x-zondicon-close class="h-5 w-5 rotate-45 form-muted-icon"/>
</button>
</div>
</fieldset>
</div>
<div>
<fieldset class="space-y-3">
<legend class="label">Links</legend>
<template x-for="(link, index) in links" :key="index">
<div class="flex gap-x-2 items-center">
<input type="text" name="links[]" x-model="link" class="input">
<button
aria-label="Remove link"
@click="links.splice(index, 1)"
>
<x-zondicon-close class="h-5 w-5 form-muted-icon"/>
</button>
</div>
</template>
<div class="flex gap-x-2 items-center">
<input
x-model="newLink"
type="url"
id="new-link"
data-test="new-link"
placeholder="https://example.com"
class="input flex-1"
spellcheck="false"
>
<button
type="button"
@click="links.push(newLink.trim()); newLink = ''"
data-test="submit-new-link-button"
:disabled="!newLink.trim().length"
aria-label="Add a new link"
>
<x-zondicon-close class="h-5 w-5 rotate-45 form-muted-icon"/>
</button>
</div>
</fieldset>
</div>
<div class="flex justify-end gap-x-5">
<button type="button" @click="$dispatch('close-modal')">Cancel</button>
<button type="submit" class="btn">Create</button>
</div>
</div>
</form>
</x-modal>
</div>
</x-layout>
SarwarAhmed liked a comment+100 XP
2mos ago
Here are the full CSS component files for those looking to copy:
resources/css/components/btn.css
button {
cursor: pointer;
}
.btn {
background: var(--color-primary);
border-radius: var(--radius-xl);
color: var(--color-primary-foreground);
padding-inline: calc(var(--spacing) * 3);
font-size: var(--text-sm);
font-weight: var(--font-weight-medium);
cursor: pointer;
height: calc(var(--spacing) * 8);
line-height: calc(var(--spacing) * 8);
display: inline-block;
}
.btn.btn-outlined {
background: transparent;
border: 1px solid var(--color-border);
border-color: var(--color-border);
color: var(--color-foreground);
}
.btn.btn-outlined:hover {
background: color-mix(in srgb, black 25%, var(--color-input));
}
.btn.btn-ghost {
background: transparent;
}
.btn:has(> svg) {
display: flex;
align-items: center;
column-gap: calc(var(--spacing) * 2);
}
.btn:hover {
background: color-mix(in srgb, black 10%, var(--color-primary));
text-decoration: none;
}
resources/css/components/form.css
label {
color: var(--color-foreground);
}
.input {
border-radius: var(--radius-md);
height: calc(var(--spacing) * 10);
width: 100%;
border-width: 1px;
border-color: var(--color-border);
padding: calc(var(--spacing) * 2) calc(var(--spacing) * 3);
background-color: var(--color-card);
color: var(--color-foreground);
outline: 2px solid transparent;
outline-offset: 2px;
}
.input::placeholder {
color: var(--color-muted-foreground);
}
.input:focus-visible {
outline: 0;
box-shadow:
0 0 0 calc(var(--spacing) * .5) var(--color-background)
0 0 0 calc(var(--spacing) * 1) var(--color-primary);
}
@media (min-width: var(--breakpoint-md)) {
.input {
font-size: var(--text-sm);
}
}
.textarea {
border-radius: var(--radius-md);
height: calc(var(--spacing) * 40);
width: 100%;
border-width: 1px;
border-color: var(--color-border);
padding: calc(var(--spacing) * 2) calc(var(--spacing) * 3);
background-color: var(--color-card);
color: var(--color-foreground);
outline: 2px solid transparent;
outline-offset: 2px;
}
.textarea::placeholder {
color: var(--color-muted-foreground);
}
.textarea:focus-visible {
outline: 0;
box-shadow:
0 0 0 calc(var(--spacing) * .5) var(--color-background)
0 0 0 calc(var(--spacing) * 1) var(--color-primary);
}
.label {
display: block;
font-size: var(--text-sm);
}
input[type=file] {
display: block;
width: 100%;
font-size: 0.875rem;
line-height: 1.25rem;
color: var(--color-foreground);
}
input[type=file]::file-selector-button {
margin-right: calc(var(--spacing) * 4);
padding: calc(var(--spacing) * 2) calc(var(--spacing) * 4);
border-radius: calc(var(--spacing) * 2);
border-width: 0;
font-size: 0.875rem;
line-height: 1.25rem;
font-weight: 500;
background-color: var(--color-primary);
color: rgb(var(--color-background));
}
input[type=file]::file-selector-button:hover {
opacity: 0.9;
}
.error {
font-size: var(--text-sm);
color: var(--color-red-600);
}
.form-muted-icon {
color: var(--color-muted-foreground);
}
.form-muted-icon:hover {
color: var(--color-foreground);
}
SarwarAhmed liked a comment+100 XP
2mos ago
<?php
declare(strict_types=1);
use Rector\Config\RectorConfig;
use Rector\TypeDeclaration\Rector\ArrowFunction\AddArrowFunctionReturnTypeRector;
use Rector\TypeDeclaration\Rector\ClassMethod\ReturnTypeFromStrictTypedCallRector;
use Rector\TypeDeclaration\Rector\ClassMethod\ReturnUnionTypeRector;
use Rector\TypeDeclaration\Rector\Closure\AddClosureVoidReturnTypeWhereNoReturnRector;
use Rector\TypeDeclaration\Rector\StmtsAwareInterface\DeclareStrictTypesRector;
use RectorLaravel\Set\LaravelSetProvider;
return RectorConfig::configure()
->withPaths([
__DIR__.'/app',
__DIR__.'/bootstrap',
__DIR__.'/config',
__DIR__.'/public',
__DIR__.'/resources',
__DIR__.'/routes',
__DIR__.'/tests',
])
->withSkip([
__DIR__.'/bootstrap/cache',
__DIR__.'/storage',
__DIR__.'/vendor',
AddClosureVoidReturnTypeWhereNoReturnRector::class,
ReturnTypeFromStrictTypedCallRector::class,
ReturnUnionTypeRector::class,
DeclareStrictTypesRector::class => [
__DIR__.'/resources/views',
],
AddArrowFunctionReturnTypeRector::class,
])
->withPhpSets()
->withSetProviders(LaravelSetProvider::class)
->withImportNames()
->withComposerBased(laravel: true)
->withPreparedSets(
deadCode: true,
codeQuality: true,
typeDeclarations: true,
privatization: true,
earlyReturn: true,
)
->withRules([
DeclareStrictTypesRector::class,
]);
SarwarAhmed liked a comment+100 XP
3mos ago
@willtomlinson Putting effort into getting the job you want is absurd? What are you talking about?
Do you really want to entertain the possibility that the 15 year old bus boy is putting more time and effort into getting a job than you? He/She has to:
- 1 hour: Shower, look presentable.
- 30 minutes: Travel to/from restaurant.
- 20 minutes: Fill out application.
- 10 minutes: Wait for manager to call you in.
- 20 minutes: Interview with manager.
We're already at 2.5 hours. What do you think @willtttomlinson? Should you spend 2.5 hours applying for a six-figure job that you'll be at for potentially years?
SarwarAhmed liked a comment+100 XP
4mos ago
@RattanakUNG I did a search a found this repo. https://github.com/isimmons/lw-webshop/tree/17d2e62f826410e1d9afd8f5287d3601f4453a04#
SarwarAhmed liked a comment+100 XP
4mos ago